diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index b6f80d9708..07dd3f7082 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -669,8 +669,8 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           Heavyweight locks, also known as lock manager locks or simply locks,
           primarily protect SQL-visible objects such as tables.  However,
           they are also used to ensure mutual exclusion for certain internal
-          operations such as relation extension.  <literal>wait_event</literal> will
-          identify the type of lock awaited.
+          operations such as waiting for a transaction to finish.
+          <literal>wait_event</literal> will identify the type of lock awaited.
          </para>
         </listitem>
         <listitem>
@@ -1122,15 +1122,11 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          execution.</entry>
         </row>
         <row>
-         <entry morerows="9"><literal>Lock</literal></entry>
+         <entry morerows="8"><literal>Lock</literal></entry>
          <entry><literal>relation</literal></entry>
          <entry>Waiting to acquire a lock on a relation.</entry>
         </row>
         <row>
-         <entry><literal>extend</literal></entry>
-         <entry>Waiting to extend a relation.</entry>
-        </row>
-        <row>
          <entry><literal>page</literal></entry>
          <entry>Waiting to acquire a lock on page of a relation.</entry>
         </row>
@@ -1263,7 +1259,7 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry>Waiting in an extension.</entry>
         </row>
         <row>
-         <entry morerows="17"><literal>IPC</literal></entry>
+         <entry morerows="18"><literal>IPC</literal></entry>
          <entry><literal>BgWorkerShutdown</literal></entry>
          <entry>Waiting for background worker to shut down.</entry>
         </row>
@@ -1320,6 +1316,10 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry>Waiting for group leader to update transaction status at transaction end.</entry>
         </row>
         <row>
+         <entry><literal>RelationExtensionLock</literal></entry>
+         <entry>Waiting to extend a relation.</entry>
+        </row>
+        <row>
          <entry><literal>ReplicationOriginDrop</literal></entry>
          <entry>Waiting for a replication origin to become inactive to be dropped.</entry>
         </row>
diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c
index 09db5c6f8f..05cca9d293 100644
--- a/src/backend/access/brin/brin_pageops.c
+++ b/src/backend/access/brin/brin_pageops.c
@@ -17,6 +17,7 @@
 #include "access/xloginsert.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
+#include "storage/extension_lock.h"
 #include "storage/freespace.h"
 #include "storage/lmgr.h"
 #include "storage/smgr.h"
@@ -623,8 +624,7 @@ brin_page_cleanup(Relation idxrel, Buffer buf)
 	 */
 	if (PageIsNew(page))
 	{
-		LockRelationForExtension(idxrel, ShareLock);
-		UnlockRelationForExtension(idxrel, ShareLock);
+		WaitForRelationExtensionLockToBeFree(idxrel);
 
 		LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 		if (PageIsNew(page))
@@ -716,7 +716,7 @@ brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
 			 */
 			if (!RELATION_IS_LOCAL(irel))
 			{
-				LockRelationForExtension(irel, ExclusiveLock);
+				LockRelationForExtension(irel);
 				extensionLockHeld = true;
 			}
 			buf = ReadBuffer(irel, P_NEW);
@@ -768,7 +768,7 @@ brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
 				}
 
 				if (extensionLockHeld)
-					UnlockRelationForExtension(irel, ExclusiveLock);
+					UnlockRelationForExtension(irel);
 
 				ReleaseBuffer(buf);
 				return InvalidBuffer;
@@ -778,7 +778,7 @@ brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
 		LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
 		if (extensionLockHeld)
-			UnlockRelationForExtension(irel, ExclusiveLock);
+			UnlockRelationForExtension(irel);
 
 		page = BufferGetPage(buf);
 
diff --git a/src/backend/access/brin/brin_revmap.c b/src/backend/access/brin/brin_revmap.c
index 03e53ce43e..af8f5ce21b 100644
--- a/src/backend/access/brin/brin_revmap.c
+++ b/src/backend/access/brin/brin_revmap.c
@@ -29,6 +29,7 @@
 #include "access/xloginsert.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
+#include "storage/extension_lock.h"
 #include "storage/lmgr.h"
 #include "utils/rel.h"
 
@@ -570,7 +571,7 @@ revmap_physical_extend(BrinRevmap *revmap)
 	else
 	{
 		if (needLock)
-			LockRelationForExtension(irel, ExclusiveLock);
+			LockRelationForExtension(irel);
 
 		buf = ReadBuffer(irel, P_NEW);
 		if (BufferGetBlockNumber(buf) != mapBlk)
@@ -582,7 +583,7 @@ revmap_physical_extend(BrinRevmap *revmap)
 			 * page from under whoever is using it.
 			 */
 			if (needLock)
-				UnlockRelationForExtension(irel, ExclusiveLock);
+				UnlockRelationForExtension(irel);
 			LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
 			ReleaseBuffer(buf);
 			return;
@@ -591,7 +592,7 @@ revmap_physical_extend(BrinRevmap *revmap)
 		page = BufferGetPage(buf);
 
 		if (needLock)
-			UnlockRelationForExtension(irel, ExclusiveLock);
+			UnlockRelationForExtension(irel);
 	}
 
 	/* Check that it's a regular block (or an empty page) */
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index d9c6483437..8d35918409 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -21,6 +21,7 @@
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/builtins.h"
@@ -325,13 +326,13 @@ GinNewBuffer(Relation index)
 	/* Must extend the file */
 	needLock = !RELATION_IS_LOCAL(index);
 	if (needLock)
-		LockRelationForExtension(index, ExclusiveLock);
+		LockRelationForExtension(index);
 
 	buffer = ReadBuffer(index, P_NEW);
 	LockBuffer(buffer, GIN_EXCLUSIVE);
 
 	if (needLock)
-		UnlockRelationForExtension(index, ExclusiveLock);
+		UnlockRelationForExtension(index);
 
 	return buffer;
 }
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index 394bc832a4..d769a76bda 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -20,6 +20,7 @@
 #include "commands/vacuum.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/memutils.h"
@@ -716,10 +717,10 @@ ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 	needLock = !RELATION_IS_LOCAL(index);
 
 	if (needLock)
-		LockRelationForExtension(index, ExclusiveLock);
+		LockRelationForExtension(index);
 	npages = RelationGetNumberOfBlocks(index);
 	if (needLock)
-		UnlockRelationForExtension(index, ExclusiveLock);
+		UnlockRelationForExtension(index);
 
 	totFreePages = 0;
 
@@ -766,10 +767,10 @@ ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 	stats->pages_free = totFreePages;
 
 	if (needLock)
-		LockRelationForExtension(index, ExclusiveLock);
+		LockRelationForExtension(index);
 	stats->num_pages = RelationGetNumberOfBlocks(index);
 	if (needLock)
-		UnlockRelationForExtension(index, ExclusiveLock);
+		UnlockRelationForExtension(index);
 
 	return stats;
 }
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index d8d1c0acfc..76171a54b9 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -20,6 +20,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "catalog/pg_opclass.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/builtins.h"
@@ -821,13 +822,13 @@ gistNewBuffer(Relation r)
 	needLock = !RELATION_IS_LOCAL(r);
 
 	if (needLock)
-		LockRelationForExtension(r, ExclusiveLock);
+		LockRelationForExtension(r);
 
 	buffer = ReadBuffer(r, P_NEW);
 	LockBuffer(buffer, GIST_EXCLUSIVE);
 
 	if (needLock)
-		UnlockRelationForExtension(r, ExclusiveLock);
+		UnlockRelationForExtension(r);
 
 	return buffer;
 }
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index 77d9d12f0b..42ef36a57e 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -18,6 +18,7 @@
 #include "access/gist_private.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 
@@ -59,10 +60,10 @@ gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 
 	/* try to find deleted pages */
 	if (needLock)
-		LockRelationForExtension(rel, ExclusiveLock);
+		LockRelationForExtension(rel);
 	npages = RelationGetNumberOfBlocks(rel);
 	if (needLock)
-		UnlockRelationForExtension(rel, ExclusiveLock);
+		UnlockRelationForExtension(rel);
 
 	totFreePages = 0;
 	for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
@@ -91,10 +92,10 @@ gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 	/* return statistics */
 	stats->pages_free = totFreePages;
 	if (needLock)
-		LockRelationForExtension(rel, ExclusiveLock);
+		LockRelationForExtension(rel);
 	stats->num_pages = RelationGetNumberOfBlocks(rel);
 	if (needLock)
-		UnlockRelationForExtension(rel, ExclusiveLock);
+		UnlockRelationForExtension(rel);
 
 	return stats;
 }
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index 13e3bdca50..fc2c9b4028 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -20,6 +20,7 @@
 #include "access/htup_details.h"
 #include "access/visibilitymap.h"
 #include "storage/bufmgr.h"
+#include "storage/extension_lock.h"
 #include "storage/freespace.h"
 #include "storage/lmgr.h"
 #include "storage/smgr.h"
@@ -186,7 +187,7 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
 	Buffer		buffer;
 
 	/* Use the length of the lock wait queue to judge how much to extend. */
-	lockWaiters = RelationExtensionLockWaiterCount(relation);
+	lockWaiters = EstimateNumberOfExtensionLockWaiters(relation);
 	if (lockWaiters <= 0)
 		return;
 
@@ -519,11 +520,11 @@ loop:
 	if (needLock)
 	{
 		if (!use_fsm)
-			LockRelationForExtension(relation, ExclusiveLock);
-		else if (!ConditionalLockRelationForExtension(relation, ExclusiveLock))
+			LockRelationForExtension(relation);
+		else if (!ConditionalLockRelationForExtension(relation))
 		{
 			/* Couldn't get the lock immediately; wait for it. */
-			LockRelationForExtension(relation, ExclusiveLock);
+			LockRelationForExtension(relation);
 
 			/*
 			 * Check if some other backend has extended a block for us while
@@ -537,7 +538,7 @@ loop:
 			 */
 			if (targetBlock != InvalidBlockNumber)
 			{
-				UnlockRelationForExtension(relation, ExclusiveLock);
+				UnlockRelationForExtension(relation);
 				goto loop;
 			}
 
@@ -576,7 +577,7 @@ loop:
 	 * against vacuumlazy.c --- see comments therein.
 	 */
 	if (needLock)
-		UnlockRelationForExtension(relation, ExclusiveLock);
+		UnlockRelationForExtension(relation);
 
 	/*
 	 * We need to initialize the empty new page.  Double-check that it really
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 4c2a13aeba..2efee686e3 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -90,6 +90,7 @@
 #include "access/xlog.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
+#include "storage/extension_lock.h"
 #include "storage/lmgr.h"
 #include "storage/smgr.h"
 #include "utils/inval.h"
@@ -641,7 +642,7 @@ vm_extend(Relation rel, BlockNumber vm_nblocks)
 	 * Note that another backend might have extended or created the relation
 	 * by the time we get the lock.
 	 */
-	LockRelationForExtension(rel, ExclusiveLock);
+	LockRelationForExtension(rel);
 
 	/* Might have to re-open if a cache flush happened */
 	RelationOpenSmgr(rel);
@@ -679,7 +680,7 @@ vm_extend(Relation rel, BlockNumber vm_nblocks)
 	/* Update local cache with the up-to-date size */
 	rel->rd_smgr->smgr_vm_nblocks = vm_nblocks_now;
 
-	UnlockRelationForExtension(rel, ExclusiveLock);
+	UnlockRelationForExtension(rel);
 
 	pfree(pg);
 }
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index c77434904e..7824c925ca 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -28,6 +28,7 @@
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "miscadmin.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
@@ -659,7 +660,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access)
 		needLock = !RELATION_IS_LOCAL(rel);
 
 		if (needLock)
-			LockRelationForExtension(rel, ExclusiveLock);
+			LockRelationForExtension(rel);
 
 		buf = ReadBuffer(rel, P_NEW);
 
@@ -673,7 +674,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access)
 		 * condition against btvacuumscan --- see comments therein.
 		 */
 		if (needLock)
-			UnlockRelationForExtension(rel, ExclusiveLock);
+			UnlockRelationForExtension(rel);
 
 		/* Initialize the new page before returning it */
 		page = BufferGetPage(buf);
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 399e6a1ae5..5af1c21d19 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -25,6 +25,7 @@
 #include "commands/vacuum.h"
 #include "pgstat.h"
 #include "storage/condition_variable.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
@@ -1058,10 +1059,10 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	{
 		/* Get the current relation length */
 		if (needLock)
-			LockRelationForExtension(rel, ExclusiveLock);
+			LockRelationForExtension(rel);
 		num_pages = RelationGetNumberOfBlocks(rel);
 		if (needLock)
-			UnlockRelationForExtension(rel, ExclusiveLock);
+			UnlockRelationForExtension(rel);
 
 		/* Quit if we've scanned the whole relation */
 		if (blkno >= num_pages)
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index bd5301f383..0ff53a3c7d 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -23,6 +23,7 @@
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/builtins.h"
+#include "storage/extension_lock.h"
 #include "utils/index_selfuncs.h"
 #include "utils/lsyscache.h"
 
@@ -230,13 +231,13 @@ SpGistNewBuffer(Relation index)
 	/* Must extend the file */
 	needLock = !RELATION_IS_LOCAL(index);
 	if (needLock)
-		LockRelationForExtension(index, ExclusiveLock);
+		LockRelationForExtension(index);
 
 	buffer = ReadBuffer(index, P_NEW);
 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 	if (needLock)
-		UnlockRelationForExtension(index, ExclusiveLock);
+		UnlockRelationForExtension(index);
 
 	return buffer;
 }
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index d7d5e90ef3..385d1cb8a2 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -24,6 +24,7 @@
 #include "commands/vacuum.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/snapmgr.h"
@@ -824,10 +825,10 @@ spgvacuumscan(spgBulkDeleteState *bds)
 	{
 		/* Get the current relation length */
 		if (needLock)
-			LockRelationForExtension(index, ExclusiveLock);
+			LockRelationForExtension(index);
 		num_pages = RelationGetNumberOfBlocks(index);
 		if (needLock)
-			UnlockRelationForExtension(index, ExclusiveLock);
+			UnlockRelationForExtension(index);
 
 		/* Quit if we've scanned the whole relation */
 		if (blkno >= num_pages)
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 20ce431e46..4a722230f1 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -54,6 +54,7 @@
 #include "portability/instr_time.h"
 #include "postmaster/autovacuum.h"
 #include "storage/bufmgr.h"
+#include "storage/extension_lock.h"
 #include "storage/freespace.h"
 #include "storage/lmgr.h"
 #include "utils/lsyscache.h"
@@ -860,8 +861,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
 			 * it's got exclusive lock on the whole relation.
 			 */
 			LockBuffer(buf, BUFFER_LOCK_UNLOCK);
-			LockRelationForExtension(onerel, ExclusiveLock);
-			UnlockRelationForExtension(onerel, ExclusiveLock);
+			WaitForRelationExtensionLockToBeFree(onerel);
 			LockBufferForCleanup(buf);
 			if (PageIsNew(page))
 			{
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 5c256ff8ab..210552ff10 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3616,6 +3616,9 @@ pgstat_get_wait_ipc(WaitEventIPC w)
 		case WAIT_EVENT_CLOG_GROUP_UPDATE:
 			event_name = "ClogGroupUpdate";
 			break;
+		case WAIT_EVENT_RELATION_EXTENSION_LOCK:
+			event_name = "RelationExtensionLock";
+			break;
 		case WAIT_EVENT_REPLICATION_ORIGIN_DROP:
 			event_name = "ReplicationOriginDrop";
 			break;
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index 4648473523..172a48c788 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -26,6 +26,7 @@
 #include "access/htup_details.h"
 #include "access/xlogutils.h"
 #include "miscadmin.h"
+#include "storage/extension_lock.h"
 #include "storage/freespace.h"
 #include "storage/fsm_internals.h"
 #include "storage/lmgr.h"
@@ -624,7 +625,7 @@ fsm_extend(Relation rel, BlockNumber fsm_nblocks)
 	 * Note that another backend might have extended or created the relation
 	 * by the time we get the lock.
 	 */
-	LockRelationForExtension(rel, ExclusiveLock);
+	LockRelationForExtension(rel);
 
 	/* Might have to re-open if a cache flush happened */
 	RelationOpenSmgr(rel);
@@ -652,7 +653,7 @@ fsm_extend(Relation rel, BlockNumber fsm_nblocks)
 	/* Update local cache with the up-to-date size */
 	rel->rd_smgr->smgr_fsm_nblocks = fsm_nblocks_now;
 
-	UnlockRelationForExtension(rel, ExclusiveLock);
+	UnlockRelationForExtension(rel);
 
 	pfree(pg);
 }
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2d1ed143e0..3b6a6f7cc2 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -35,6 +35,7 @@
 #include "replication/origin.h"
 #include "storage/bufmgr.h"
 #include "storage/dsm.h"
+#include "storage/extension_lock.h"
 #include "storage/ipc.h"
 #include "storage/pg_shmem.h"
 #include "storage/pmsignal.h"
@@ -133,6 +134,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 		size = add_size(size, BackgroundWorkerShmemSize());
 		size = add_size(size, MultiXactShmemSize());
 		size = add_size(size, LWLockShmemSize());
+		size = add_size(size, RelExtLockShmemSize());
 		size = add_size(size, ProcArrayShmemSize());
 		size = add_size(size, BackendStatusShmemSize());
 		size = add_size(size, SInvalShmemSize());
@@ -235,6 +237,11 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 	InitPredicateLocks();
 
 	/*
+	 * Set up relation extension lock manager
+	 */
+	InitRelExtLocks();
+
+	/*
 	 * Set up process table
 	 */
 	if (!IsUnderPostmaster)
diff --git a/src/backend/storage/lmgr/Makefile b/src/backend/storage/lmgr/Makefile
index e1b787e838..2334a40875 100644
--- a/src/backend/storage/lmgr/Makefile
+++ b/src/backend/storage/lmgr/Makefile
@@ -13,7 +13,7 @@ top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = lmgr.o lock.o proc.o deadlock.o lwlock.o lwlocknames.o spin.o \
-	s_lock.o predicate.o condition_variable.o
+	s_lock.o predicate.o condition_variable.o extension_lock.o
 
 include $(top_srcdir)/src/backend/common.mk
 
diff --git a/src/backend/storage/lmgr/README b/src/backend/storage/lmgr/README
index 56b0a12a3e..960d1f3f09 100644
--- a/src/backend/storage/lmgr/README
+++ b/src/backend/storage/lmgr/README
@@ -3,7 +3,7 @@ src/backend/storage/lmgr/README
 Locking Overview
 ================
 
-Postgres uses four types of interprocess locks:
+Postgres uses five types of interprocess locks:
 
 * Spinlocks.  These are intended for *very* short-term locks.  If a lock
 is to be held more than a few dozen instructions, or across any sort of
@@ -36,13 +36,21 @@ Regular locks should be used for all user-driven lock requests.
 
 * SIReadLock predicate locks.  See separate README-SSI file for details.
 
+* Relation extension locks. Only one process can extend a relation at
+a time; we use a specialized lock manager for this purpose, which is
+much simpler than the regular lock manager.  It is similar to the
+lightweight lock mechanism, but is ever simpler because there is only
+one lock mode and only one lock can be taken at a time. A process holding
+a relation extension lock is interruptible, unlike a process holding an
+LWLock.
+
 Acquisition of either a spinlock or a lightweight lock causes query
 cancel and die() interrupts to be held off until all such locks are
 released. No such restriction exists for regular locks, however.  Also
 note that we can accept query cancel and die() interrupts while waiting
-for a regular lock, but we will not accept them while waiting for
-spinlocks or LW locks. It is therefore not a good idea to use LW locks
-when the wait time might exceed a few seconds.
+for a relation extension lock or a regular lock, but we will not accept
+them while waiting for spinlocks or LW locks. It is therefore not a good
+idea to use LW locks when the wait time might exceed a few seconds.
 
 The rest of this README file discusses the regular lock manager in detail.
 
diff --git a/src/backend/storage/lmgr/extension_lock.c b/src/backend/storage/lmgr/extension_lock.c
new file mode 100644
index 0000000000..67ca763ce4
--- /dev/null
+++ b/src/backend/storage/lmgr/extension_lock.c
@@ -0,0 +1,469 @@
+/*-------------------------------------------------------------------------
+ *
+ * extension_lock.c
+ *	  Relation extension lock manager
+ *
+ * This specialized lock manager is used only for relation extension
+ * locks.  Unlike the heavyweight lock manager, it doesn't provide
+ * deadlock detection or group locking.  Unlike lwlock.c, extension lock
+ * waits are interruptible.  Unlike both systems, there is only one lock
+ * mode.
+ *
+ * False sharing is possible.  We have a fixed-size array of locks, and
+ * every database OID/relation OID combination is mapped to a slot in
+ * the array.  Therefore, if two processes try to extend relations that
+ * map to the same array slot, they will contend even though it would
+ * be OK to let both proceed at once.  Since these locks are typically
+ * taken only for very short periods of time, this doesn't seem likely
+ * to be a big problem in practice.  If it is, we could make the array
+ * bigger.
+ *
+ * The extension lock manager is much faster than the regular heavyweight
+ * lock manager.  The lack of group locking is a feature, not a bug,
+ * because while cooperating backends can all (for example) access a
+ * relation on which they jointly hold AccessExclusiveLock at the same time,
+ * it's not safe for them to extend the relation at the same time.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *		src/backend/storage/lmgr/extension_lock.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "catalog/catalog.h"
+#include "postmaster/postmaster.h"
+#include "storage/extension_lock.h"
+#include "utils/rel.h"
+
+#define N_RELEXTLOCK_ENTS		1024
+
+/*
+ * We can't use bit 31 as the lock bit because pg_atomic_sub_fetch_u32 can't
+ * handle an attempt to subtract INT_MIN.
+ */
+#define RELEXT_LOCK_BIT			((uint32) 1 << 30)
+#define RELEXT_WAIT_COUNT_MASK	(RELEXT_LOCK_BIT - 1)
+
+typedef struct RelExtLockTag
+{
+	Oid			dbid;			/* InvalidOid for a shared relation */
+	Oid			relid;
+} RelExtLockTag;
+
+typedef struct RelExtLock
+{
+	pg_atomic_uint32 state;
+	ConditionVariable cv;
+} RelExtLock;
+
+/*
+ * Backend-private state for relation extension locks.  "relid" is the last
+ * relation whose RelExtLock we looked up, and "lock" is a pointer to the
+ * RelExtLock to which it mapped.  This speeds up the fairly common case where
+ * we acquire the same relation extension lock repeatedly.  nLocks is 0 is the
+ * number of times we've acquired that lock; 0 means we don't hold it, while
+ * any value >0 means we do.
+ *
+ * A backend can't hold more than one relation extension lock at the same
+ * time, although it can hold the same lock more than once.  Sometimes we try
+ * to acquire a lock for additional forks while already holding the lock for
+ * the main fork; for example, this might happen when adding extra relation
+ * blocks for both relation and its free space map. But since this lock
+ * manager doesn't distinguish between the forks, we just increment nLocks in
+ * the case.
+ */
+typedef struct relextlock_handle
+{
+	Oid			relid;
+	RelExtLock *lock;
+	int			nLocks;			/* > 0 means holding it */
+	bool		waiting;		/* true if we're waiting it */
+} relextlock_handle;
+
+static relextlock_handle held_relextlock;
+static RelExtLock *RelExtLockArray;
+
+static bool RelExtLockAcquire(Oid relid, bool conditional);
+static bool RelExtLockAttemptLock(RelExtLock *relextlock);
+static void RelExtLockRelease(void);
+static inline uint32 RelExtLockTargetTagToIndex(RelExtLockTag *locktag);
+
+/*
+ * Estimate space required for a fixed-size array of RelExtLock structures.
+ */
+Size
+RelExtLockShmemSize(void)
+{
+	return mul_size(N_RELEXTLOCK_ENTS, sizeof(RelExtLock));
+}
+
+/*
+ * Initialize extension lock manager.
+ */
+void
+InitRelExtLocks(void)
+{
+	bool		found;
+	int			i;
+
+	/* Verify that we have enough bits for maximum possible waiter count. */
+	StaticAssertStmt(RELEXT_WAIT_COUNT_MASK >= MAX_BACKENDS,
+					 "maximum waiter count of relation extension lock exceeds MAX_BACKENDS");
+
+	RelExtLockArray = (RelExtLock *)
+		ShmemInitStruct("Relation Extension Lock",
+						N_RELEXTLOCK_ENTS * sizeof(RelExtLock),
+						&found);
+
+	/* we're the first - initialize */
+	if (!found)
+	{
+		for (i = 0; i < N_RELEXTLOCK_ENTS; i++)
+		{
+			RelExtLock *relextlock = &RelExtLockArray[i];
+
+			pg_atomic_init_u32(&(relextlock->state), 0);
+			ConditionVariableInit(&(relextlock->cv));
+		}
+	}
+}
+
+/*
+ * This lock is used to interlock addition of pages to relations.
+ * We need such locking because bufmgr/smgr definition of P_NEW is not
+ * race-condition-proof.
+ *
+ * We assume the caller is already holding some type of regular lock on
+ * the relation, so no AcceptInvalidationMessages call is needed here.
+ */
+void
+LockRelationForExtension(Relation relation)
+{
+	RelExtLockAcquire(RelationGetRelid(relation), false);
+}
+
+/*
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE iff the lock was acquired.
+ */
+bool
+ConditionalLockRelationForExtension(Relation relation)
+{
+	return RelExtLockAcquire(RelationGetRelid(relation), true);
+}
+
+/*
+ * Estimate the number of processes waiting for the given relation extension
+ * lock. Note that since multiple relations hash to the same RelExtLock entry,
+ * the return value might be inflated.
+ */
+int
+EstimateNumberOfExtensionLockWaiters(Relation relation)
+{
+	RelExtLockTag tag;
+	RelExtLock *relextlock;
+	uint32		state;
+	Oid			relid = RelationGetRelid(relation);
+
+	/* Make a lock tag */
+	tag.dbid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId;
+	tag.relid = relid;
+
+	relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+	state = pg_atomic_read_u32(&(relextlock->state));
+
+	return (state & RELEXT_WAIT_COUNT_MASK);
+}
+
+/*
+ * Release a previously-acquired extension lock.
+ */
+void
+UnlockRelationForExtension(Relation relation)
+{
+	Oid			relid = RelationGetRelid(relation);
+
+	if (held_relextlock.nLocks <= 0 || relid != held_relextlock.relid)
+	{
+		elog(WARNING,
+			 "relation extension lock for %u is not held",
+			 relid);
+		return;
+	}
+
+	/*
+	 * If we acquired it multiple times, only change shared state when we have
+	 * released it as many times as we acquired it.
+	 */
+	if (--held_relextlock.nLocks == 0)
+		RelExtLockRelease();
+}
+
+/*
+ * Release any extension lock held, and any wait count for an extension lock.
+ * This is intended to be invoked during error cleanup.
+ */
+void
+RelExtLockCleanup(void)
+{
+	if (held_relextlock.nLocks > 0)
+	{
+		/* Release the lock even if we acquired it multiple times. */
+		held_relextlock.nLocks = 0;
+		RelExtLockRelease();
+		Assert(!held_relextlock.waiting);
+	}
+	else if (held_relextlock.waiting)
+	{
+		/* We were waiting for the lock; release the wait count we held. */
+		held_relextlock.waiting = false;
+		pg_atomic_sub_fetch_u32(&(held_relextlock.lock->state), 1);
+	}
+}
+
+/*
+ * Are we holding any extension lock?
+ */
+bool
+IsAnyRelationExtensionLockHeld(void)
+{
+	return held_relextlock.nLocks > 0;
+}
+
+/*
+ *		WaitForRelationExtensionLockToBeFree
+ *
+ * Wait for the relation extension lock on the given relation to
+ * be free without acquiring it.
+ */
+void
+WaitForRelationExtensionLockToBeFree(Relation relation)
+{
+	RelExtLock *relextlock;
+	Oid			relid;
+
+	relid = RelationGetRelid(relation);
+
+	if (held_relextlock.nLocks > 0)
+	{
+		/*
+		 * If we already hold the lock, nobody else does, so we can return
+		 * immediately.
+		 */
+		if (relid == held_relextlock.relid)
+			return;
+		elog(ERROR,
+			 "can only manipulate one relation extension lock at a time");
+	}
+
+	/*
+	 * If the last relation extension lock we touched is the same one for
+	 * which we now need to wait, we can use our cached pointer to the lock
+	 * instead of recomputing it.
+	 */
+	if (relid == held_relextlock.relid)
+		relextlock = held_relextlock.lock;
+	else
+	{
+		RelExtLockTag tag;
+
+		tag.dbid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId;
+		tag.relid = relid;
+		relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+		held_relextlock.relid = relid;
+		held_relextlock.lock = relextlock;
+	}
+
+	for (;;)
+	{
+		uint32		state;
+
+		state = pg_atomic_read_u32(&(relextlock)->state);
+
+		/* Break if nobody is holding the lock on this relation */
+		if ((state & RELEXT_LOCK_BIT) == 0)
+			break;
+
+		/* Could not get the lock, prepare to wait */
+		if (!held_relextlock.waiting)
+		{
+			pg_atomic_add_fetch_u32(&(relextlock->state), 1);
+			held_relextlock.waiting = true;
+		}
+
+		/* Sleep until something happens, then recheck */
+		ConditionVariableSleep(&(relextlock->cv),
+							   WAIT_EVENT_RELATION_EXTENSION_LOCK);
+	}
+
+	ConditionVariableCancelSleep();
+
+	/* Release any wait count we hold */
+	if (held_relextlock.waiting)
+	{
+		pg_atomic_sub_fetch_u32(&(relextlock->state), 1);
+		held_relextlock.waiting = false;
+	}
+}
+
+/*
+ * Compute the hash code associated with a RelExtLock.
+ */
+static inline uint32
+RelExtLockTargetTagToIndex(RelExtLockTag *locktag)
+{
+	return tag_hash(locktag, sizeof(RelExtLockTag)) % N_RELEXTLOCK_ENTS;
+}
+
+/*
+ * Acquire a relation extension lock.
+ */
+static bool
+RelExtLockAcquire(Oid relid, bool conditional)
+{
+	RelExtLock *relextlock;
+	bool		mustwait;
+
+	/*
+	 * If we already hold the lock, we can just increase the count locally.
+	 * Since we don't do deadlock detection, caller must not try to take a new
+	 * relation extension lock while already holding them.
+	 */
+	if (held_relextlock.nLocks > 0)
+	{
+		if (relid != held_relextlock.relid)
+			elog(ERROR,
+				 "can only acquire one relation extension lock at a time");
+
+		held_relextlock.nLocks++;
+		return true;
+	}
+
+	/*
+	 * If the last relation extension lock we touched is the same one for we
+	 * now need to acquire, we can use our cached pointer to the lock instead
+	 * of recomputing it.  This is likely to be a common case in practice.
+	 */
+	if (relid == held_relextlock.relid)
+		relextlock = held_relextlock.lock;
+	else
+	{
+		RelExtLockTag tag;
+
+		/* Make a lock tag */
+		tag.dbid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId;
+		tag.relid = relid;
+
+		relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+
+		/* Remember the lock we're interested in */
+		held_relextlock.relid = relid;
+		held_relextlock.lock = relextlock;
+	}
+
+	held_relextlock.waiting = false;
+	for (;;)
+	{
+		mustwait = RelExtLockAttemptLock(relextlock);
+
+		if (!mustwait)
+			break;				/* got the lock */
+
+		/* Could not got the lock, return iff in locking conditionally */
+		if (conditional)
+			return false;
+
+		/* Could not get the lock, prepare to wait */
+		if (!held_relextlock.waiting)
+		{
+			pg_atomic_add_fetch_u32(&(relextlock->state), 1);
+			held_relextlock.waiting = true;
+		}
+
+		/* Sleep until something happens, then recheck */
+		ConditionVariableSleep(&(relextlock->cv),
+							   WAIT_EVENT_RELATION_EXTENSION_LOCK);
+	}
+
+	ConditionVariableCancelSleep();
+
+	/* Release any wait count we hold */
+	if (held_relextlock.waiting)
+	{
+		pg_atomic_sub_fetch_u32(&(relextlock->state), 1);
+		held_relextlock.waiting = false;
+	}
+
+	Assert(!mustwait);
+
+	/* Remember lock held by this backend */
+	held_relextlock.relid = relid;
+	held_relextlock.lock = relextlock;
+	held_relextlock.nLocks = 1;
+
+	/* We got the lock! */
+	return true;
+}
+
+/*
+ * Attempt to atomically acquire the relation extension lock.
+ *
+ * Returns true if the lock isn't free and we need to wait.
+ */
+static bool
+RelExtLockAttemptLock(RelExtLock *relextlock)
+{
+	uint32		oldstate;
+
+	oldstate = pg_atomic_read_u32(&relextlock->state);
+
+	while (true)
+	{
+		bool		lock_free;
+
+		lock_free = (oldstate & RELEXT_LOCK_BIT) == 0;
+
+		if (!lock_free)
+			return true;
+
+		if (pg_atomic_compare_exchange_u32(&relextlock->state,
+										   &oldstate,
+										   oldstate | RELEXT_LOCK_BIT))
+			return false;
+	}
+
+	pg_unreachable();
+}
+
+/*
+ * Release extension lock in shared memory.  Should be called when our local
+ * lock count drops to 0.
+ */
+static void
+RelExtLockRelease(void)
+{
+	RelExtLock *relextlock;
+	uint32		state;
+	uint32		wait_counts;
+
+	Assert(held_relextlock.nLocks == 0);
+
+	relextlock = held_relextlock.lock;
+
+	/* Release the lock */
+	state = pg_atomic_sub_fetch_u32(&(relextlock->state), RELEXT_LOCK_BIT);
+
+	/* If there may be waiters, wake them up */
+	wait_counts = state & RELEXT_WAIT_COUNT_MASK;
+
+	if (wait_counts > 0)
+		ConditionVariableBroadcast(&(relextlock->cv));
+}
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index da5679b7a3..4fbc0c4a67 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -319,78 +319,6 @@ UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
 }
 
 /*
- *		LockRelationForExtension
- *
- * This lock tag is used to interlock addition of pages to relations.
- * We need such locking because bufmgr/smgr definition of P_NEW is not
- * race-condition-proof.
- *
- * We assume the caller is already holding some type of regular lock on
- * the relation, so no AcceptInvalidationMessages call is needed here.
- */
-void
-LockRelationForExtension(Relation relation, LOCKMODE lockmode)
-{
-	LOCKTAG		tag;
-
-	SET_LOCKTAG_RELATION_EXTEND(tag,
-								relation->rd_lockInfo.lockRelId.dbId,
-								relation->rd_lockInfo.lockRelId.relId);
-
-	(void) LockAcquire(&tag, lockmode, false, false);
-}
-
-/*
- *		ConditionalLockRelationForExtension
- *
- * As above, but only lock if we can get the lock without blocking.
- * Returns true iff the lock was acquired.
- */
-bool
-ConditionalLockRelationForExtension(Relation relation, LOCKMODE lockmode)
-{
-	LOCKTAG		tag;
-
-	SET_LOCKTAG_RELATION_EXTEND(tag,
-								relation->rd_lockInfo.lockRelId.dbId,
-								relation->rd_lockInfo.lockRelId.relId);
-
-	return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
-}
-
-/*
- *		RelationExtensionLockWaiterCount
- *
- * Count the number of processes waiting for the given relation extension lock.
- */
-int
-RelationExtensionLockWaiterCount(Relation relation)
-{
-	LOCKTAG		tag;
-
-	SET_LOCKTAG_RELATION_EXTEND(tag,
-								relation->rd_lockInfo.lockRelId.dbId,
-								relation->rd_lockInfo.lockRelId.relId);
-
-	return LockWaiterCount(&tag);
-}
-
-/*
- *		UnlockRelationForExtension
- */
-void
-UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
-{
-	LOCKTAG		tag;
-
-	SET_LOCKTAG_RELATION_EXTEND(tag,
-								relation->rd_lockInfo.lockRelId.dbId,
-								relation->rd_lockInfo.lockRelId.relId);
-
-	LockRelease(&tag, lockmode, false);
-}
-
-/*
  *		LockPage
  *
  * Obtain a page-level lock.  This is currently used by some index access
@@ -961,12 +889,6 @@ DescribeLockTag(StringInfo buf, const LOCKTAG *tag)
 							 tag->locktag_field2,
 							 tag->locktag_field1);
 			break;
-		case LOCKTAG_RELATION_EXTEND:
-			appendStringInfo(buf,
-							 _("extension of relation %u of database %u"),
-							 tag->locktag_field2,
-							 tag->locktag_field1);
-			break;
 		case LOCKTAG_PAGE:
 			appendStringInfo(buf,
 							 _("page %u of relation %u of database %u"),
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 5833086c62..5a623fa1ed 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -40,6 +40,7 @@
 #include "miscadmin.h"
 #include "pg_trace.h"
 #include "pgstat.h"
+#include "storage/extension_lock.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "storage/sinvaladt.h"
@@ -717,6 +718,13 @@ LockAcquireExtended(const LOCKTAG *locktag,
 	int			status;
 	bool		log_lock = false;
 
+	/*
+	 * Relation extension locks don't participate in deadlock detection,
+	 * so make sure we don't try to acquire a heavyweight lock while
+	 * holding one.
+	 */
+	Assert(!IsAnyRelationExtensionLockHeld());
+
 	if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
 		elog(ERROR, "unrecognized lock method: %d", lockmethodid);
 	lockMethodTable = LockMethods[lockmethodid];
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 5f6727d501..9167767a24 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -44,6 +44,7 @@
 #include "replication/slot.h"
 #include "replication/syncrep.h"
 #include "storage/condition_variable.h"
+#include "storage/extension_lock.h"
 #include "storage/standby.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
@@ -765,6 +766,8 @@ ProcReleaseLocks(bool isCommit)
 		return;
 	/* If waiting, get off wait queue (should only be needed after error) */
 	LockErrorCleanup();
+	/* Release any relation extension lock or wait counts */
+	RelExtLockCleanup();
 	/* Release standard locks, including session-level if aborting */
 	LockReleaseAll(DEFAULT_LOCKMETHOD, !isCommit);
 	/* Release transaction-level advisory locks */
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 9e0a8ab79d..6d8916c200 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -25,7 +25,6 @@
 /* This must match enum LockTagType! */
 const char *const LockTagTypeNames[] = {
 	"relation",
-	"extend",
 	"page",
 	"tuple",
 	"transactionid",
@@ -234,7 +233,6 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		switch ((LockTagType) instance->locktag.locktag_type)
 		{
 			case LOCKTAG_RELATION:
-			case LOCKTAG_RELATION_EXTEND:
 				values[1] = ObjectIdGetDatum(instance->locktag.locktag_field1);
 				values[2] = ObjectIdGetDatum(instance->locktag.locktag_field2);
 				nulls[3] = true;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 089b7c3a10..b3611c3e22 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -813,6 +813,7 @@ typedef enum
 	WAIT_EVENT_PARALLEL_BITMAP_SCAN,
 	WAIT_EVENT_PROCARRAY_GROUP_UPDATE,
 	WAIT_EVENT_CLOG_GROUP_UPDATE,
+	WAIT_EVENT_RELATION_EXTENSION_LOCK,
 	WAIT_EVENT_REPLICATION_ORIGIN_DROP,
 	WAIT_EVENT_REPLICATION_SLOT_DROP,
 	WAIT_EVENT_SAFE_SNAPSHOT,
diff --git a/src/include/storage/extension_lock.h b/src/include/storage/extension_lock.h
new file mode 100644
index 0000000000..850f19c316
--- /dev/null
+++ b/src/include/storage/extension_lock.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * extension_lock.h
+ *	  Relation extension lock manager
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/extension_lock.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef EXTENSION_LOCK_H
+#define EXTENSION_LOCK_H
+
+#ifdef FRONTEND
+#error "extension_lock.h may not be included from frontend code"
+#endif
+
+#include "port/atomics.h"
+#include "storage/s_lock.h"
+#include "storage/condition_variable.h"
+#include "storage/proclist_types.h"
+
+/* Lock a relation for extension */
+extern Size RelExtLockShmemSize(void);
+extern void InitRelExtLocks(void);
+extern void LockRelationForExtension(Relation relation);
+extern void UnlockRelationForExtension(Relation relation);
+extern bool ConditionalLockRelationForExtension(Relation relation);
+extern int	EstimateNumberOfExtensionLockWaiters(Relation relation);
+extern void WaitForRelationExtensionLockToBeFree(Relation relation);
+extern void RelExtLockCleanup(void);
+extern bool	IsAnyRelationExtensionLockHeld(void);
+
+#endif	/* EXTENSION_LOCK_H */
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index 0b923227a2..7e6b80c78a 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -50,13 +50,6 @@ extern bool LockHasWaitersRelation(Relation relation, LOCKMODE lockmode);
 extern void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode);
 extern void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode);
 
-/* Lock a relation for extension */
-extern void LockRelationForExtension(Relation relation, LOCKMODE lockmode);
-extern void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode);
-extern bool ConditionalLockRelationForExtension(Relation relation,
-									LOCKMODE lockmode);
-extern int	RelationExtensionLockWaiterCount(Relation relation);
-
 /* Lock a page (currently only used within indexes) */
 extern void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
 extern bool ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 765431e299..3be18ea1c5 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -138,8 +138,6 @@ typedef uint16 LOCKMETHODID;
 typedef enum LockTagType
 {
 	LOCKTAG_RELATION,			/* whole relation */
-	/* ID info for a relation is DB OID + REL OID; DB OID = 0 if shared */
-	LOCKTAG_RELATION_EXTEND,	/* the right to extend a relation */
 	/* same ID info as RELATION */
 	LOCKTAG_PAGE,				/* one page of a relation */
 	/* ID info for a page is RELATION info + BlockNumber */
@@ -198,14 +196,6 @@ typedef struct LOCKTAG
 	 (locktag).locktag_type = LOCKTAG_RELATION, \
 	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
 
-#define SET_LOCKTAG_RELATION_EXTEND(locktag,dboid,reloid) \
-	((locktag).locktag_field1 = (dboid), \
-	 (locktag).locktag_field2 = (reloid), \
-	 (locktag).locktag_field3 = 0, \
-	 (locktag).locktag_field4 = 0, \
-	 (locktag).locktag_type = LOCKTAG_RELATION_EXTEND, \
-	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
-
 #define SET_LOCKTAG_PAGE(locktag,dboid,reloid,blocknum) \
 	((locktag).locktag_field1 = (dboid), \
 	 (locktag).locktag_field2 = (reloid), \
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 72eb9fd390..4fb5b91441 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1834,6 +1834,8 @@ RegisNode
 RegisteredBgWorker
 ReindexObjectType
 ReindexStmt
+RelExtLock
+RelExtLockTag
 RelFileNode
 RelFileNodeBackend
 RelIdCacheEnt
@@ -2956,6 +2958,7 @@ registered_buffer
 regmatch_t
 regoff_t
 regproc
+relextlock_handle
 relopt_bool
 relopt_gen
 relopt_int
