diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index b6f80d9..07dd3f7 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 09db5c6..05cca9d 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 03e53ce..af8f5ce 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 d9c6483..8d35918 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 394bc83..d769a76 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 d8d1c0a..76171a5 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 77d9d12..42ef36a 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 13e3bdc..9287f2d 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"
@@ -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 4c2a13a..2efee68 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 c774349..7824c92 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 399e6a1..5af1c21 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 bd5301f..0ff53a3 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 d7d5e90..385d1cb 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 20ce431..4a72223 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 5c256ff..210552f 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 4648473..172a48c 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 2d1ed14..3b6a6f7 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 e1b787e..2334a40 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 56b0a12..960d1f3 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 0000000..1299c0e
--- /dev/null
+++ b/src/backend/storage/lmgr/extension_lock.c
@@ -0,0 +1,483 @@
+/*-------------------------------------------------------------------------
+ *
+ * extension_lock.c
+ *	  Relation extension lock manager
+ *
+ * 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
+ *
+ * NOTES:
+ *
+ * This lock manager is specialized in relation extension locks; light
+ * weight and interruptible lock manager. It's similar to heavy-weight
+ * lock but doesn't have dead lock detection mechanism, group locking
+ * mechanism and multiple lock modes.
+ *
+ * The entries for relation extension locks are allocated on the shared
+ * memory as an array. The pair of database id and relation id maps to
+ * one of them by hashing.
+ *
+ * For lock acquisition we use an atomic compare-and-exchange on the
+ * state variable. When a process tries to acquire a lock that conflicts
+ * with existing lock, it is put to sleep using condition variables
+ * if not conditional locking. When release the lock, we use an atomic
+ * decrement to release the lock.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/extension_lock.h"
+#include "utils/rel.h"
+
+/* The total entries of relation extension lock on shared memory */
+#define N_RELEXTLOCK_ENTS 1024
+
+#define RELEXT_LOCK_BIT		((uint32) ((1 << 25)))
+
+/* Must be greater than MAX_BACKENDS - which is 2^23-1, so we're fine. */
+#define RELEXT_WAIT_COUNT_MASK	((uint32) ((1 << 24) - 1))
+
+/* This tag maps to one of entries on the RelExtLockArray array by hashing */
+typedef struct RelExtLockTag
+{
+	Oid		dbid;
+	Oid		relid;
+} RelExtLockTag;
+
+typedef struct RelExtLock
+{
+	pg_atomic_uint32	state; 	/* state of exclusive lock */
+	ConditionVariable	cv;
+} RelExtLock;
+
+/*
+ * This structure holds information per-object relation extension
+ * lock. "lock" variable represents the RelExtLockArray we are
+ * holding, waiting for or had been holding before. If we're holding
+ * a relation extension lock on a relation, nLocks > 0. nLocks == 0
+ * means that we don't hold any locks. We use this structure to keep
+ * track of holding relation extension locks, and to also store it
+ * as a cache. So when releasing the lock we don't invalidate the lock
+ * variable. We check the cache first, and then use it without touching
+ * RelExtLockArray if the relation extension lock is the same as what
+ * we just touched.
+ *
+ * At most one lock can be held at once. Note that sometimes we
+ * could try to acquire a lock for the additional forks while holding
+ * the lock for the main fork; for example, 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;
+
+/* Pointer to array containing relation extension lock states */
+static RelExtLock *RelExtLockArray;
+
+static bool RelExtLockAcquire(Oid relid, bool conditional);
+static void RelExtLockRelease(Oid rleid, bool force);
+static bool RelExtLockAttemptLock(RelExtLock *relextlock);
+static inline uint32 RelExtLockTargetTagToIndex(RelExtLockTag *locktag);
+
+Size
+RelExtLockShmemSize(void)
+{
+	/* Relation extension locks array */
+	return mul_size(N_RELEXTLOCK_ENTS, sizeof(RelExtLock));
+}
+
+/*
+ * InitRelExtLock
+ *      Initialize the relation extension lock manager's data structures.
+ */
+void
+InitRelExtLocks(void)
+{
+	Size	size;
+	bool	found;
+	int		i;
+
+	size = mul_size(N_RELEXTLOCK_ENTS, sizeof(RelExtLock));
+	RelExtLockArray = (RelExtLock *)
+		ShmemInitStruct("Relation Extension Lock", size, &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));
+		}
+	}
+}
+
+/*
+ *		LockRelationForExtension
+ *
+ * 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(relation->rd_id, 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)
+{
+	return RelExtLockAcquire(relation->rd_id, true);
+}
+
+/*
+ *		RelationExtensionLockWaiterCount
+ *
+ * Count the number of processes waiting for the given relation extension
+ * lock. Note that since the lock for multiple relations uses the same
+ * RelExtLock entry, the return value might not be accurate.
+ */
+int
+RelationExtensionLockWaiterCount(Relation relation)
+{
+	RelExtLockTag tag;
+	RelExtLock	*relextlock;
+	uint32		state;
+
+	/* Make a lock tag */
+	tag.dbid = MyDatabaseId;
+	tag.relid = RelationGetRelid(relation);
+
+	relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+	state = pg_atomic_read_u32(&(relextlock->state));
+
+	return (state & RELEXT_WAIT_COUNT_MASK);
+}
+
+/*
+ *		UnlockRelationForExtension
+ */
+void
+UnlockRelationForExtension(Relation relation)
+{
+	RelExtLockRelease(relation->rd_id, false);
+}
+
+/*
+ *		RelationExtensionLockReleaseAll
+ *
+ * release all currently-held relation extension locks
+ */
+void
+RelExtLockReleaseAll(void)
+{
+	if (held_relextlock.nLocks > 0)
+		RelExtLockRelease(held_relextlock.relid, true);
+	else if (held_relextlock.waiting)
+	{
+		/*
+		 * Decrement the ref counts if we don't hold the lock but
+		 * was waiting for the lock.
+		 */
+		pg_atomic_sub_fetch_u32(&(held_relextlock.lock->state), 1);
+	}
+}
+
+/*
+ *		IsAnyRelationExtensionLockHeld
+ *
+ * Return true if we're holding relation extension locks.
+ */
+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 we already hold the lock, no need to wait */
+	if (held_relextlock.nLocks > 0 && relid == held_relextlock.relid)
+		return;
+
+	/*
+	 * 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;
+
+		/* Make a lock tag */
+		tag.dbid = MyDatabaseId;
+		tag.relid = relid;
+
+		relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+
+		/* Remember the lock we're interested in */
+		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 the lock is released */
+		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;
+	}
+
+	return;
+}
+
+/*
+ * Compute the hash code associated with a RelExtLock.
+ *
+ * To avoid unnecessary recomputations of the hash code, we try to do this
+ * just once per function, and then pass it around as needed.  we can
+ * extract the index number of RelExtLockArray.
+ */
+static inline uint32
+RelExtLockTargetTagToIndex(RelExtLockTag *locktag)
+{
+	return (tag_hash((const void *) 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,
+				 "cannot acquire relation extension locks for multiple relations at the same");
+
+		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 = MyDatabaseId;
+		tag.relid = relid;
+
+		relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+
+		/* Remeber 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 the lock is released */
+		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;
+}
+
+/*
+ * RelExtLockRelease
+ *
+ * Release a previously acquired relation extension lock. If force is
+ * true, we release the all holding locks on the given relation.
+ */
+static void
+RelExtLockRelease(Oid relid, bool force)
+{
+	RelExtLock	*relextlock;
+	uint32	state;
+	uint32	wait_counts;
+
+	/* We should have acquired a lock before releasing */
+	Assert(held_relextlock.nLocks > 0);
+
+	if (relid != held_relextlock.relid)
+		ereport(ERROR,
+				(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+				 errmsg("relation extension lock for %u is not held",
+						relid)));
+
+	/* If force releasing, release all locks we're holding */
+	if (force)
+		held_relextlock.nLocks = 0;
+	else
+		held_relextlock.nLocks--;
+
+	Assert(held_relextlock.nLocks >= 0);
+
+	/* Return if we're still holding the lock even after computation */
+	if (held_relextlock.nLocks > 0)
+		return;
+
+	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));
+}
+
+/*
+ * Internal function that attempts 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)
+	{
+		uint32	desired_state;
+		bool	lock_free;
+
+		desired_state = oldstate;
+
+		lock_free = (oldstate & RELEXT_LOCK_BIT) == 0;
+		if (lock_free)
+			desired_state += RELEXT_LOCK_BIT;
+
+		if (pg_atomic_compare_exchange_u32(&relextlock->state,
+										   &oldstate, desired_state))
+		{
+			if (lock_free)
+				return false;
+			else
+				return true;
+		}
+	}
+	pg_unreachable();
+}
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index da5679b..4fbc0c4 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 5833086..3eaf8cb 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() == 0);
+
 	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 5f6727d..b1b0c63 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();
+	/* If we hold a relation extension lock, release it */
+	RelExtLockReleaseAll();
 	/* 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 9e0a8ab..6d8916c 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 089b7c3..b3611c3 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 0000000..27fda42
--- /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	RelationExtensionLockWaiterCount(Relation relation);
+extern void WaitForRelationExtensionLockToBeFree(Relation relation);
+extern void RelExtLockReleaseAll(void);
+extern bool	IsAnyRelationExtensionLockHeld(void);
+
+#endif	/* EXTENSION_LOCK_H */
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index 0b92322..7e6b80c 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 765431e..3be18ea 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), \
