v1-0001-Disallow-external-access-to-MaxBackends.patch

application/octet-stream

Filename: v1-0001-Disallow-external-access-to-MaxBackends.patch
Type: application/octet-stream
Part: 0
Message: Re: make MaxBackends available in _PG_init

Patch

Format: format-patch
Series: patch v1-0001
Subject: Disallow external access to MaxBackends.
File+
src/backend/access/nbtree/nbtutils.c 2 2
src/backend/access/transam/multixact.c 1 1
src/backend/access/transam/twophase.c 1 1
src/backend/bootstrap/bootstrap.c 1 1
src/backend/commands/async.c 5 5
src/backend/libpq/pqcomm.c 1 1
src/backend/postmaster/postmaster.c 6 8
src/backend/storage/ipc/dsm.c 1 1
src/backend/storage/ipc/procarray.c 1 1
src/backend/storage/ipc/procsignal.c 1 1
src/backend/storage/ipc/sinvaladt.c 2 2
src/backend/storage/lmgr/deadlock.c 16 15
src/backend/storage/lmgr/lock.c 10 10
src/backend/storage/lmgr/predicate.c 5 5
src/backend/storage/lmgr/proc.c 8 9
src/backend/utils/activity/backend_status.c 5 5
src/backend/utils/adt/lockfuncs.c 2 2
src/backend/utils/init/postinit.c 39 10
src/include/miscadmin.h 11 1
From 67bc167c81cef9b582e37f81c59c4ebd0cbb3a8f Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Tue, 3 Aug 2021 23:05:55 +0000
Subject: [PATCH v1 1/1] Disallow external access to MaxBackends.

Presently, MaxBackends is externally visible, but it may still be
uninitialized in places where it would be convenient to use (e.g.,
_PG_init()).  This change makes MaxBackends static to postinit.c to
disallow such direct access.  Instead, MaxBackends should now be
accessed via GetMaxBackends().  GetMaxBackends() also accepts a
couple of flags to easily include commonly added values such as
max_prepared_transactions.

Separately, adjust the comments about needing to register
background workers before initializing MaxBackends.  Since
6bc8ef0b, InitializeMaxBackends() has used max_worker_processes
instead of tallying up the number of registered background workers,
so background worker registration is no longer a prerequisite.  The
ordering of this logic is still useful for allowing libraries to
adjust GUCs, so the comments have been updated to mention that use-
case.
---
 src/backend/access/nbtree/nbtutils.c        |  4 +--
 src/backend/access/transam/multixact.c      |  2 +-
 src/backend/access/transam/twophase.c       |  2 +-
 src/backend/bootstrap/bootstrap.c           |  2 +-
 src/backend/commands/async.c                | 10 +++---
 src/backend/libpq/pqcomm.c                  |  2 +-
 src/backend/postmaster/postmaster.c         | 14 ++++-----
 src/backend/storage/ipc/dsm.c               |  2 +-
 src/backend/storage/ipc/procarray.c         |  2 +-
 src/backend/storage/ipc/procsignal.c        |  2 +-
 src/backend/storage/ipc/sinvaladt.c         |  4 +--
 src/backend/storage/lmgr/deadlock.c         | 31 +++++++++---------
 src/backend/storage/lmgr/lock.c             | 20 ++++++------
 src/backend/storage/lmgr/predicate.c        | 10 +++---
 src/backend/storage/lmgr/proc.c             | 17 +++++-----
 src/backend/utils/activity/backend_status.c | 10 +++---
 src/backend/utils/adt/lockfuncs.c           |  4 +--
 src/backend/utils/init/postinit.c           | 49 +++++++++++++++++++++++------
 src/include/miscadmin.h                     | 12 ++++++-
 19 files changed, 118 insertions(+), 81 deletions(-)

diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index d524310723..578d4531a2 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -2065,7 +2065,7 @@ BTreeShmemSize(void)
 	Size		size;
 
 	size = offsetof(BTVacInfo, vacuums);
-	size = add_size(size, mul_size(MaxBackends, sizeof(BTOneVacInfo)));
+	size = add_size(size, mul_size(GetMaxBackends(0), sizeof(BTOneVacInfo)));
 	return size;
 }
 
@@ -2094,7 +2094,7 @@ BTreeShmemInit(void)
 		btvacinfo->cycle_ctr = (BTCycleId) time(NULL);
 
 		btvacinfo->num_vacuums = 0;
-		btvacinfo->max_vacuums = MaxBackends;
+		btvacinfo->max_vacuums = GetMaxBackends(0);
 	}
 	else
 		Assert(found);
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index e6c70ed0bc..7e9728bf88 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -285,7 +285,7 @@ typedef struct MultiXactStateData
  * Last element of OldestMemberMXactId and OldestVisibleMXactId arrays.
  * Valid elements are (1..MaxOldestSlot); element 0 is never used.
  */
-#define MaxOldestSlot	(MaxBackends + max_prepared_xacts)
+#define MaxOldestSlot	(GetMaxBackends(GMB_MAX_PREPARED_XACTS))
 
 /* Pointers to the state data in shared memory */
 static MultiXactStateData *MultiXactState;
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 6d3efb49a4..1ba84983f2 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -293,7 +293,7 @@ TwoPhaseShmemInit(void)
 			 * prepared transaction. Currently multixact.c uses that
 			 * technique.
 			 */
-			gxacts[i].dummyBackendId = MaxBackends + 1 + i;
+			gxacts[i].dummyBackendId = GetMaxBackends(0) + 1 + i;
 		}
 	}
 	else
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 67cd5ac6e9..ceb1983544 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -395,7 +395,7 @@ AuxiliaryProcessMain(int argc, char *argv[])
 		 * This will need rethinking if we ever want more than one of a
 		 * particular auxiliary process type.
 		 */
-		ProcSignalInit(MaxBackends + MyAuxProcType + 1);
+		ProcSignalInit(GetMaxBackends(0) + MyAuxProcType + 1);
 
 		/* finish setting up bufmgr.c */
 		InitBufferPoolBackend();
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 4b16fb5682..29c50595c2 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -511,7 +511,7 @@ AsyncShmemSize(void)
 	Size		size;
 
 	/* This had better match AsyncShmemInit */
-	size = mul_size(MaxBackends + 1, sizeof(QueueBackendStatus));
+	size = mul_size(GetMaxBackends(0) + 1, sizeof(QueueBackendStatus));
 	size = add_size(size, offsetof(AsyncQueueControl, backend));
 
 	size = add_size(size, SimpleLruShmemSize(NUM_NOTIFY_BUFFERS, 0));
@@ -534,7 +534,7 @@ AsyncShmemInit(void)
 	 * The used entries in the backend[] array run from 1 to MaxBackends; the
 	 * zero'th entry is unused but must be allocated.
 	 */
-	size = mul_size(MaxBackends + 1, sizeof(QueueBackendStatus));
+	size = mul_size(GetMaxBackends(0) + 1, sizeof(QueueBackendStatus));
 	size = add_size(size, offsetof(AsyncQueueControl, backend));
 
 	asyncQueueControl = (AsyncQueueControl *)
@@ -549,7 +549,7 @@ AsyncShmemInit(void)
 		QUEUE_FIRST_LISTENER = InvalidBackendId;
 		asyncQueueControl->lastQueueFillWarn = 0;
 		/* zero'th entry won't be used, but let's initialize it anyway */
-		for (int i = 0; i <= MaxBackends; i++)
+		for (int i = 0; i <= GetMaxBackends(0); i++)
 		{
 			QUEUE_BACKEND_PID(i) = InvalidPid;
 			QUEUE_BACKEND_DBOID(i) = InvalidOid;
@@ -1685,8 +1685,8 @@ SignalBackends(void)
 	 * preallocate the arrays?	But in practice this is only run in trivial
 	 * transactions, so there should surely be space available.
 	 */
-	pids = (int32 *) palloc(MaxBackends * sizeof(int32));
-	ids = (BackendId *) palloc(MaxBackends * sizeof(BackendId));
+	pids = (int32 *) palloc(GetMaxBackends(0) * sizeof(int32));
+	ids = (BackendId *) palloc(GetMaxBackends(0) * sizeof(BackendId));
 	count = 0;
 
 	LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 89a5f901aa..4d74f9a3f5 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -556,7 +556,7 @@ StreamServerPort(int family, const char *hostName, unsigned short portNumber,
 		 * intended to provide a clamp on the request on platforms where an
 		 * overly large request provokes a kernel error (are there any?).
 		 */
-		maxconn = MaxBackends * 2;
+		maxconn = GetMaxBackends(0) * 2;
 		if (maxconn > PG_SOMAXCONN)
 			maxconn = PG_SOMAXCONN;
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 00d051d520..bf05b15455 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -990,10 +990,8 @@ PostmasterMain(int argc, char *argv[])
 	LocalProcessControlFile(false);
 
 	/*
-	 * Register the apply launcher.  Since it registers a background worker,
-	 * it needs to be called before InitializeMaxBackends(), and it's probably
-	 * a good idea to call it before any modules had chance to take the
-	 * background worker slots.
+	 * Register the apply launcher.  It's probably a good idea to call it before
+	 * any modules had a chance to take the background worker slots.
 	 */
 	ApplyLauncherRegister();
 
@@ -1014,8 +1012,8 @@ PostmasterMain(int argc, char *argv[])
 #endif
 
 	/*
-	 * Now that loadable modules have had their chance to register background
-	 * workers, calculate MaxBackends.
+	 * Now that loadable modules have had their chance to alter any GUCs,
+	 * calculate MaxBackends.
 	 */
 	InitializeMaxBackends();
 
@@ -6184,7 +6182,7 @@ save_backend_variables(BackendParameters *param, Port *port,
 	param->query_id_enabled = query_id_enabled;
 	param->max_safe_fds = max_safe_fds;
 
-	param->MaxBackends = MaxBackends;
+	param->MaxBackends = GetMaxBackends(0);
 
 #ifdef WIN32
 	param->PostmasterHandle = PostmasterHandle;
@@ -6418,7 +6416,7 @@ restore_backend_variables(BackendParameters *param, Port *port)
 	query_id_enabled = param->query_id_enabled;
 	max_safe_fds = param->max_safe_fds;
 
-	MaxBackends = param->MaxBackends;
+	SetMaxBackends(param->MaxBackends);
 
 #ifdef WIN32
 	PostmasterHandle = param->PostmasterHandle;
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index b461a5f7e9..bb279bb8d1 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -165,7 +165,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 
 	/* Determine size for new control segment. */
 	maxitems = PG_DYNSHMEM_FIXED_SLOTS
-		+ PG_DYNSHMEM_SLOTS_PER_BACKEND * MaxBackends;
+		+ PG_DYNSHMEM_SLOTS_PER_BACKEND * GetMaxBackends(0);
 	elog(DEBUG2, "dynamic shared memory system will support %u segments",
 		 maxitems);
 	segsize = dsm_control_bytes_needed(maxitems);
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index c7816fcfb3..9afd0209c8 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -365,7 +365,7 @@ ProcArrayShmemSize(void)
 	Size		size;
 
 	/* Size of the ProcArray structure itself */
-#define PROCARRAY_MAXPROCS	(MaxBackends + max_prepared_xacts)
+#define PROCARRAY_MAXPROCS	(GetMaxBackends(GMB_MAX_PREPARED_XACTS))
 
 	size = offsetof(ProcArrayStruct, pgprocnos);
 	size = add_size(size, mul_size(sizeof(int), PROCARRAY_MAXPROCS));
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index defb75aa26..86b7c39782 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -85,7 +85,7 @@ typedef struct
  * possible auxiliary process type.  (This scheme assumes there is not
  * more than one of any auxiliary process type at a time.)
  */
-#define NumProcSignalSlots	(MaxBackends + NUM_AUXPROCTYPES)
+#define NumProcSignalSlots	(GetMaxBackends(0) + NUM_AUXPROCTYPES)
 
 /* Check whether the relevant type bit is set in the flags. */
 #define BARRIER_SHOULD_CHECK(flags, type) \
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index 946bd8e3cb..2e85a99f68 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -205,7 +205,7 @@ SInvalShmemSize(void)
 	Size		size;
 
 	size = offsetof(SISeg, procState);
-	size = add_size(size, mul_size(sizeof(ProcState), MaxBackends));
+	size = add_size(size, mul_size(sizeof(ProcState), GetMaxBackends(0)));
 
 	return size;
 }
@@ -231,7 +231,7 @@ CreateSharedInvalidationState(void)
 	shmInvalBuffer->maxMsgNum = 0;
 	shmInvalBuffer->nextThreshold = CLEANUP_MIN;
 	shmInvalBuffer->lastBackend = 0;
-	shmInvalBuffer->maxBackends = MaxBackends;
+	shmInvalBuffer->maxBackends = GetMaxBackends(0);
 	SpinLockInit(&shmInvalBuffer->msgnumLock);
 
 	/* The buffer[] array is initially all unused, so we need not fill it */
diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c
index 67733c0d1a..6ffc481009 100644
--- a/src/backend/storage/lmgr/deadlock.c
+++ b/src/backend/storage/lmgr/deadlock.c
@@ -143,6 +143,7 @@ void
 InitDeadLockChecking(void)
 {
 	MemoryContext oldcxt;
+	int max_backends = GetMaxBackends(0);
 
 	/* Make sure allocations are permanent */
 	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
@@ -151,16 +152,16 @@ InitDeadLockChecking(void)
 	 * FindLockCycle needs at most MaxBackends entries in visitedProcs[] and
 	 * deadlockDetails[].
 	 */
-	visitedProcs = (PGPROC **) palloc(MaxBackends * sizeof(PGPROC *));
-	deadlockDetails = (DEADLOCK_INFO *) palloc(MaxBackends * sizeof(DEADLOCK_INFO));
+	visitedProcs = (PGPROC **) palloc(max_backends * sizeof(PGPROC *));
+	deadlockDetails = (DEADLOCK_INFO *) palloc(max_backends * sizeof(DEADLOCK_INFO));
 
 	/*
 	 * TopoSort needs to consider at most MaxBackends wait-queue entries, and
 	 * it needn't run concurrently with FindLockCycle.
 	 */
 	topoProcs = visitedProcs;	/* re-use this space */
-	beforeConstraints = (int *) palloc(MaxBackends * sizeof(int));
-	afterConstraints = (int *) palloc(MaxBackends * sizeof(int));
+	beforeConstraints = (int *) palloc(max_backends * sizeof(int));
+	afterConstraints = (int *) palloc(max_backends * sizeof(int));
 
 	/*
 	 * We need to consider rearranging at most MaxBackends/2 wait queues
@@ -169,8 +170,8 @@ InitDeadLockChecking(void)
 	 * MaxBackends total waiters.
 	 */
 	waitOrders = (WAIT_ORDER *)
-		palloc((MaxBackends / 2) * sizeof(WAIT_ORDER));
-	waitOrderProcs = (PGPROC **) palloc(MaxBackends * sizeof(PGPROC *));
+		palloc((max_backends / 2) * sizeof(WAIT_ORDER));
+	waitOrderProcs = (PGPROC **) palloc(max_backends * sizeof(PGPROC *));
 
 	/*
 	 * Allow at most MaxBackends distinct constraints in a configuration. (Is
@@ -180,7 +181,7 @@ InitDeadLockChecking(void)
 	 * limits the maximum recursion depth of DeadLockCheckRecurse. Making it
 	 * really big might potentially allow a stack-overflow problem.
 	 */
-	maxCurConstraints = MaxBackends;
+	maxCurConstraints = max_backends;
 	curConstraints = (EDGE *) palloc(maxCurConstraints * sizeof(EDGE));
 
 	/*
@@ -191,7 +192,7 @@ InitDeadLockChecking(void)
 	 * last MaxBackends entries in possibleConstraints[] are reserved as
 	 * output workspace for FindLockCycle.
 	 */
-	maxPossibleConstraints = MaxBackends * 4;
+	maxPossibleConstraints = max_backends * 4;
 	possibleConstraints =
 		(EDGE *) palloc(maxPossibleConstraints * sizeof(EDGE));
 
@@ -327,7 +328,7 @@ DeadLockCheckRecurse(PGPROC *proc)
 	if (nCurConstraints >= maxCurConstraints)
 		return true;			/* out of room for active constraints? */
 	oldPossibleConstraints = nPossibleConstraints;
-	if (nPossibleConstraints + nEdges + MaxBackends <= maxPossibleConstraints)
+	if (nPossibleConstraints + nEdges + GetMaxBackends(0) <= maxPossibleConstraints)
 	{
 		/* We can save the edge list in possibleConstraints[] */
 		nPossibleConstraints += nEdges;
@@ -388,7 +389,7 @@ TestConfiguration(PGPROC *startProc)
 	/*
 	 * Make sure we have room for FindLockCycle's output.
 	 */
-	if (nPossibleConstraints + MaxBackends > maxPossibleConstraints)
+	if (nPossibleConstraints + GetMaxBackends(0) > maxPossibleConstraints)
 		return -1;
 
 	/*
@@ -486,7 +487,7 @@ FindLockCycleRecurse(PGPROC *checkProc,
 				 * record total length of cycle --- outer levels will now fill
 				 * deadlockDetails[]
 				 */
-				Assert(depth <= MaxBackends);
+				Assert(depth <= GetMaxBackends(0));
 				nDeadlockDetails = depth;
 
 				return true;
@@ -500,7 +501,7 @@ FindLockCycleRecurse(PGPROC *checkProc,
 		}
 	}
 	/* Mark proc as seen */
-	Assert(nVisitedProcs < MaxBackends);
+	Assert(nVisitedProcs < GetMaxBackends(0));
 	visitedProcs[nVisitedProcs++] = checkProc;
 
 	/*
@@ -698,7 +699,7 @@ FindLockCycleRecurseMember(PGPROC *checkProc,
 					/*
 					 * Add this edge to the list of soft edges in the cycle
 					 */
-					Assert(*nSoftEdges < MaxBackends);
+					Assert(*nSoftEdges < GetMaxBackends(0));
 					softEdges[*nSoftEdges].waiter = checkProcLeader;
 					softEdges[*nSoftEdges].blocker = leader;
 					softEdges[*nSoftEdges].lock = lock;
@@ -771,7 +772,7 @@ FindLockCycleRecurseMember(PGPROC *checkProc,
 					/*
 					 * Add this edge to the list of soft edges in the cycle
 					 */
-					Assert(*nSoftEdges < MaxBackends);
+					Assert(*nSoftEdges < GetMaxBackends(0));
 					softEdges[*nSoftEdges].waiter = checkProcLeader;
 					softEdges[*nSoftEdges].blocker = leader;
 					softEdges[*nSoftEdges].lock = lock;
@@ -834,7 +835,7 @@ ExpandConstraints(EDGE *constraints,
 		waitOrders[nWaitOrders].procs = waitOrderProcs + nWaitOrderProcs;
 		waitOrders[nWaitOrders].nProcs = lock->waitProcs.size;
 		nWaitOrderProcs += lock->waitProcs.size;
-		Assert(nWaitOrderProcs <= MaxBackends);
+		Assert(nWaitOrderProcs <= GetMaxBackends(0));
 
 		/*
 		 * Do the topo sort.  TopoSort need not examine constraints after this
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 364654e106..581bbc29d5 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -55,7 +55,7 @@
 int			max_locks_per_xact; /* set by guc.c */
 
 #define NLOCKENTS() \
-	mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
+	mul_size(max_locks_per_xact, GetMaxBackends(GMB_MAX_PREPARED_XACTS))
 
 
 /*
@@ -2938,12 +2938,12 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
 			vxids = (VirtualTransactionId *)
 				MemoryContextAlloc(TopMemoryContext,
 								   sizeof(VirtualTransactionId) *
-								   (MaxBackends + max_prepared_xacts + 1));
+								   (GetMaxBackends(GMB_MAX_PREPARED_XACTS) + 1));
 	}
 	else
 		vxids = (VirtualTransactionId *)
 			palloc0(sizeof(VirtualTransactionId) *
-					(MaxBackends + max_prepared_xacts + 1));
+					(GetMaxBackends(GMB_MAX_PREPARED_XACTS) + 1));
 
 	/* Compute hash code and partition lock, and look up conflicting modes. */
 	hashcode = LockTagHashCode(locktag);
@@ -3100,7 +3100,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
 
 	LWLockRelease(partitionLock);
 
-	if (count > MaxBackends + max_prepared_xacts)	/* should never happen */
+	if (count > GetMaxBackends(GMB_MAX_PREPARED_XACTS))	/* should never happen */
 		elog(PANIC, "too many conflicting locks found");
 
 	vxids[count].backendId = InvalidBackendId;
@@ -3651,7 +3651,7 @@ GetLockStatusData(void)
 	data = (LockData *) palloc(sizeof(LockData));
 
 	/* Guess how much space we'll need. */
-	els = MaxBackends;
+	els = GetMaxBackends(0);
 	el = 0;
 	data->locks = (LockInstanceData *) palloc(sizeof(LockInstanceData) * els);
 
@@ -3685,7 +3685,7 @@ GetLockStatusData(void)
 
 			if (el >= els)
 			{
-				els += MaxBackends;
+				els += GetMaxBackends(0);
 				data->locks = (LockInstanceData *)
 					repalloc(data->locks, sizeof(LockInstanceData) * els);
 			}
@@ -3717,7 +3717,7 @@ GetLockStatusData(void)
 
 			if (el >= els)
 			{
-				els += MaxBackends;
+				els += GetMaxBackends(0);
 				data->locks = (LockInstanceData *)
 					repalloc(data->locks, sizeof(LockInstanceData) * els);
 			}
@@ -3846,7 +3846,7 @@ GetBlockerStatusData(int blocked_pid)
 	 * for the procs[] array; the other two could need enlargement, though.)
 	 */
 	data->nprocs = data->nlocks = data->npids = 0;
-	data->maxprocs = data->maxlocks = data->maxpids = MaxBackends;
+	data->maxprocs = data->maxlocks = data->maxpids = GetMaxBackends(0);
 	data->procs = (BlockedProcData *) palloc(sizeof(BlockedProcData) * data->maxprocs);
 	data->locks = (LockInstanceData *) palloc(sizeof(LockInstanceData) * data->maxlocks);
 	data->waiter_pids = (int *) palloc(sizeof(int) * data->maxpids);
@@ -3949,7 +3949,7 @@ GetSingleProcBlockerStatusData(PGPROC *blocked_proc, BlockedProcsData *data)
 
 		if (data->nlocks >= data->maxlocks)
 		{
-			data->maxlocks += MaxBackends;
+			data->maxlocks += GetMaxBackends(0);
 			data->locks = (LockInstanceData *)
 				repalloc(data->locks, sizeof(LockInstanceData) * data->maxlocks);
 		}
@@ -3978,7 +3978,7 @@ GetSingleProcBlockerStatusData(PGPROC *blocked_proc, BlockedProcsData *data)
 
 	if (queue_size > data->maxpids - data->npids)
 	{
-		data->maxpids = Max(data->maxpids + MaxBackends,
+		data->maxpids = Max(data->maxpids + GetMaxBackends(0),
 							data->npids + queue_size);
 		data->waiter_pids = (int *) repalloc(data->waiter_pids,
 											 sizeof(int) * data->maxpids);
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 56267bdc3c..d6199aa82d 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -257,7 +257,7 @@
 	(&MainLWLockArray[PREDICATELOCK_MANAGER_LWLOCK_OFFSET + (i)].lock)
 
 #define NPREDICATELOCKTARGETENTS() \
-	mul_size(max_predicate_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
+	mul_size(max_predicate_locks_per_xact, GetMaxBackends(GMB_MAX_PREPARED_XACTS))
 
 #define SxactIsOnFinishedList(sxact) (!SHMQueueIsDetached(&((sxact)->finishedLink)))
 
@@ -1222,7 +1222,7 @@ InitPredicateLocks(void)
 	 * Compute size for serializable transaction hashtable. Note these
 	 * calculations must agree with PredicateLockShmemSize!
 	 */
-	max_table_size = (MaxBackends + max_prepared_xacts);
+	max_table_size = (GetMaxBackends(GMB_MAX_PREPARED_XACTS));
 
 	/*
 	 * Allocate a list to hold information on transactions participating in
@@ -1374,7 +1374,7 @@ PredicateLockShmemSize(void)
 	size = add_size(size, size / 10);
 
 	/* transaction list */
-	max_table_size = MaxBackends + max_prepared_xacts;
+	max_table_size = GetMaxBackends(GMB_MAX_PREPARED_XACTS);
 	max_table_size *= 10;
 	size = add_size(size, PredXactListDataSize);
 	size = add_size(size, mul_size((Size) max_table_size,
@@ -1905,7 +1905,7 @@ GetSerializableTransactionSnapshotInt(Snapshot snapshot,
 	{
 		++(PredXact->WritableSxactCount);
 		Assert(PredXact->WritableSxactCount <=
-			   (MaxBackends + max_prepared_xacts));
+			   (GetMaxBackends(GMB_MAX_PREPARED_XACTS)));
 	}
 
 	MySerializableXact = sxact;
@@ -5107,7 +5107,7 @@ predicatelock_twophase_recover(TransactionId xid, uint16 info,
 		{
 			++(PredXact->WritableSxactCount);
 			Assert(PredXact->WritableSxactCount <=
-				   (MaxBackends + max_prepared_xacts));
+				   (GetMaxBackends(GMB_MAX_PREPARED_XACTS)));
 		}
 
 		/*
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index b7d9da0aa9..9600afec16 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -102,8 +102,7 @@ Size
 ProcGlobalShmemSize(void)
 {
 	Size		size = 0;
-	Size		TotalProcs =
-	add_size(MaxBackends, add_size(NUM_AUXILIARY_PROCS, max_prepared_xacts));
+	Size		TotalProcs = GetMaxBackends(GMB_NUM_AUXILIARY_PROCS | GMB_MAX_PREPARED_XACTS);
 
 	/* ProcGlobal */
 	size = add_size(size, sizeof(PROC_HDR));
@@ -127,7 +126,7 @@ ProcGlobalSemas(void)
 	 * We need a sema per backend (including autovacuum), plus one for each
 	 * auxiliary process.
 	 */
-	return MaxBackends + NUM_AUXILIARY_PROCS;
+	return GetMaxBackends(GMB_NUM_AUXILIARY_PROCS);
 }
 
 /*
@@ -162,7 +161,7 @@ InitProcGlobal(void)
 	int			i,
 				j;
 	bool		found;
-	uint32		TotalProcs = MaxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts;
+	uint32		TotalProcs = GetMaxBackends(GMB_NUM_AUXILIARY_PROCS | GMB_MAX_PREPARED_XACTS);
 
 	/* Create the ProcGlobal shared structure */
 	ProcGlobal = (PROC_HDR *)
@@ -197,7 +196,7 @@ InitProcGlobal(void)
 	MemSet(procs, 0, TotalProcs * sizeof(PGPROC));
 	ProcGlobal->allProcs = procs;
 	/* XXX allProcCount isn't really all of them; it excludes prepared xacts */
-	ProcGlobal->allProcCount = MaxBackends + NUM_AUXILIARY_PROCS;
+	ProcGlobal->allProcCount = GetMaxBackends(GMB_NUM_AUXILIARY_PROCS);
 
 	/*
 	 * Allocate arrays mirroring PGPROC fields in a dense manner. See
@@ -223,7 +222,7 @@ InitProcGlobal(void)
 		 * dummy PGPROCs don't need these though - they're never associated
 		 * with a real process
 		 */
-		if (i < MaxBackends + NUM_AUXILIARY_PROCS)
+		if (i < GetMaxBackends(GMB_NUM_AUXILIARY_PROCS))
 		{
 			procs[i].sem = PGSemaphoreCreate();
 			InitSharedLatch(&(procs[i].procLatch));
@@ -260,7 +259,7 @@ InitProcGlobal(void)
 			ProcGlobal->bgworkerFreeProcs = &procs[i];
 			procs[i].procgloballist = &ProcGlobal->bgworkerFreeProcs;
 		}
-		else if (i < MaxBackends)
+		else if (i < GetMaxBackends(0))
 		{
 			/* PGPROC for walsender, add to walsenderFreeProcs list */
 			procs[i].links.next = (SHM_QUEUE *) ProcGlobal->walsenderFreeProcs;
@@ -288,8 +287,8 @@ InitProcGlobal(void)
 	 * Save pointers to the blocks of PGPROC structures reserved for auxiliary
 	 * processes and prepared transactions.
 	 */
-	AuxiliaryProcs = &procs[MaxBackends];
-	PreparedXactProcs = &procs[MaxBackends + NUM_AUXILIARY_PROCS];
+	AuxiliaryProcs = &procs[GetMaxBackends(0)];
+	PreparedXactProcs = &procs[GetMaxBackends(GMB_NUM_AUXILIARY_PROCS)];
 
 	/* Create ProcStructLock spinlock, too */
 	ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t));
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index 2901f9f5a9..4ff834bbc5 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -35,7 +35,7 @@
  * includes autovacuum workers and background workers as well.
  * ----------
  */
-#define NumBackendStatSlots (MaxBackends + NUM_AUXPROCTYPES)
+#define NumBackendStatSlots (GetMaxBackends(0) + NUM_AUXPROCTYPES)
 
 
 /* ----------
@@ -251,7 +251,7 @@ pgstat_beinit(void)
 	/* Initialize MyBEEntry */
 	if (MyBackendId != InvalidBackendId)
 	{
-		Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
+		Assert(MyBackendId >= 1 && MyBackendId <= GetMaxBackends(0));
 		MyBEEntry = &BackendStatusArray[MyBackendId - 1];
 	}
 	else
@@ -267,7 +267,7 @@ pgstat_beinit(void)
 		 * MaxBackends + AuxBackendType + 1 as the index of the slot for an
 		 * auxiliary process.
 		 */
-		MyBEEntry = &BackendStatusArray[MaxBackends + MyAuxProcType];
+		MyBEEntry = &BackendStatusArray[GetMaxBackends(0) + MyAuxProcType];
 	}
 
 	/* Set up a process-exit hook to clean up */
@@ -892,7 +892,7 @@ pgstat_get_backend_current_activity(int pid, bool checkUser)
 	int			i;
 
 	beentry = BackendStatusArray;
-	for (i = 1; i <= MaxBackends; i++)
+	for (i = 1; i <= GetMaxBackends(0); i++)
 	{
 		/*
 		 * Although we expect the target backend's entry to be stable, that
@@ -978,7 +978,7 @@ pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
 	if (beentry == NULL || BackendActivityBuffer == NULL)
 		return NULL;
 
-	for (i = 1; i <= MaxBackends; i++)
+	for (i = 1; i <= GetMaxBackends(0); i++)
 	{
 		if (beentry->st_procpid == pid)
 		{
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 5dc0a5882c..d262965800 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -561,11 +561,11 @@ pg_safe_snapshot_blocking_pids(PG_FUNCTION_ARGS)
 	Datum	   *blocker_datums;
 
 	/* A buffer big enough for any possible blocker list without truncation */
-	blockers = (int *) palloc(MaxBackends * sizeof(int));
+	blockers = (int *) palloc(GetMaxBackends(0) * sizeof(int));
 
 	/* Collect a snapshot of processes waited for by GetSafeSnapshot */
 	num_blockers =
-		GetSafeSnapshotBlockingPids(blocked_pid, blockers, MaxBackends);
+		GetSafeSnapshotBlockingPids(blocked_pid, blockers, GetMaxBackends(0));
 
 	/* Convert int array to Datum array */
 	if (num_blockers > 0)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 51d1bbef30..bf02bc0283 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -25,6 +25,7 @@
 #include "access/session.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
+#include "access/twophase.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
@@ -63,6 +64,9 @@
 #include "utils/syscache.h"
 #include "utils/timeout.h"
 
+static int MaxBackends = 0;
+static int MaxBackendsInitialized = false;
+
 static HeapTuple GetDatabaseTuple(const char *dbname);
 static HeapTuple GetDatabaseTupleByOid(Oid dboid);
 static void PerformAuthentication(Port *port);
@@ -502,9 +506,8 @@ pg_split_opts(char **argv, int *argcp, const char *optstr)
 /*
  * Initialize MaxBackends value from config options.
  *
- * This must be called after modules have had the chance to register background
- * workers in shared_preload_libraries, and before shared memory size is
- * determined.
+ * This must be called after modules have had the change to alter GUCs in
+ * shared_preload_libraries, and before shared memory size is determined.
  *
  * Note that in EXEC_BACKEND environment, the value is passed down from
  * postmaster to subprocesses via BackendParameters in SubPostmasterMain; only
@@ -514,15 +517,41 @@ pg_split_opts(char **argv, int *argcp, const char *optstr)
 void
 InitializeMaxBackends(void)
 {
-	Assert(MaxBackends == 0);
-
 	/* the extra unit accounts for the autovacuum launcher */
-	MaxBackends = MaxConnections + autovacuum_max_workers + 1 +
-		max_worker_processes + max_wal_senders;
+	SetMaxBackends(MaxConnections + autovacuum_max_workers + 1 +
+		max_worker_processes + max_wal_senders);
+}
+
+int
+GetMaxBackends(uint32 flags)
+{
+	int ret;
+
+	if (!MaxBackendsInitialized)
+		elog(ERROR, "MaxBackends not yet initialized");
+
+	ret = MaxBackends;
+
+	if (flags & GMB_MAX_PREPARED_XACTS)
+		ret += max_prepared_xacts;
 
-	/* internal error because the values were all checked previously */
-	if (MaxBackends > MAX_BACKENDS)
+	if (flags & GMB_NUM_AUXILIARY_PROCS)
+		ret += NUM_AUXILIARY_PROCS;
+
+	return ret;
+}
+
+void
+SetMaxBackends(int max_backends)
+{
+	if (MaxBackendsInitialized)
+		elog(ERROR, "MaxBackends already initialized");
+
+	if (max_backends > MAX_BACKENDS)
 		elog(ERROR, "too many backends configured");
+
+	MaxBackends = max_backends;
+	MaxBackendsInitialized = true;
 }
 
 /*
@@ -603,7 +632,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 
 	SharedInvalBackendInit(false);
 
-	if (MyBackendId > MaxBackends || MyBackendId <= 0)
+	if (MyBackendId > GetMaxBackends(0) || MyBackendId <= 0)
 		elog(FATAL, "bad backend ID: %d", MyBackendId);
 
 	/* Now that we have a BackendId, we can participate in ProcSignal */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 68d840d699..67902d2541 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -172,7 +172,6 @@ extern PGDLLIMPORT char *DataDir;
 extern PGDLLIMPORT int data_directory_mode;
 
 extern PGDLLIMPORT int NBuffers;
-extern PGDLLIMPORT int MaxBackends;
 extern PGDLLIMPORT int MaxConnections;
 extern PGDLLIMPORT int max_worker_processes;
 extern PGDLLIMPORT int max_parallel_workers;
@@ -455,9 +454,20 @@ extern AuxProcType MyAuxProcType;
  *			POSTGRES initialization and cleanup definitions.                 *
  *****************************************************************************/
 
+/*
+ * Option flag bits for GetMaxBackends().
+ */
+typedef enum GMBOption
+{
+	GMB_MAX_PREPARED_XACTS = 1 << 0,	/* include max_prepared_xacts */
+	GMB_NUM_AUXILIARY_PROCS = 1 << 1	/* include NUM_AUXILIARY_PROCS */
+} GMBOption;
+
 /* in utils/init/postinit.c */
 extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
 extern void InitializeMaxBackends(void);
+extern int GetMaxBackends(uint32 flags);
+extern void SetMaxBackends(int max_backends);
 extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 						 Oid useroid, char *out_dbname, bool override_allow_connections);
 extern void BaseInit(void);
-- 
2.16.6