diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 8d461c8..cffc70d 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -675,6 +675,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
         </listitem>
         <listitem>
          <para>
+          <literal>RelationExtensionLock</literal>: The backend is waiting for
+          a relation extension lock. This lock protects a particular relation
+          such as table against concurrent relation extension.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
           <literal>BufferPin</literal>: The server process is waiting to access to
           a data buffer during a period when no other process can be
           examining that buffer.  Buffer pin waits can be protracted if
@@ -1158,6 +1165,11 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry>Waiting to acquire an advisory user lock.</entry>
         </row>
         <row>
+         <entry><literal>RelationExtensionLock</literal></entry>
+         <entry><literal>RelationExtensionLock</literal></entry>
+         <entry>Waiting to acquire a relation extension lock on a relation.</entry>
+        </row>
+        <row>
          <entry><literal>BufferPin</literal></entry>
          <entry><literal>BufferPin</literal></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
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/discard.c b/src/backend/commands/discard.c
index f0dcd87..4a470eb 100644
--- a/src/backend/commands/discard.c
+++ b/src/backend/commands/discard.c
@@ -19,6 +19,7 @@
 #include "commands/discard.h"
 #include "commands/prepare.h"
 #include "commands/sequence.h"
+#include "storage/extension_lock.h"
 #include "utils/guc.h"
 #include "utils/portal.h"
 
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..d12777a 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3351,6 +3351,9 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case PG_WAIT_LOCK:
 			event_type = "Lock";
 			break;
+		case PG_WAIT_RELEXTLOCK:
+			event_type = "RelationExtensionLock";
+			break;
 		case PG_WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
@@ -3408,6 +3411,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case PG_WAIT_LOCK:
 			event_name = GetLockNameFromTagType(eventId);
 			break;
+		case PG_WAIT_RELEXTLOCK:
+			event_name = "RelationExtensionLock";
+			break;
 		case PG_WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			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..a322296 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 exntesion 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..71eb293 100644
--- a/src/backend/storage/lmgr/README
+++ b/src/backend/storage/lmgr/README
@@ -29,6 +29,13 @@ process has to wait for an LWLock, it blocks on a SysV semaphore so as
 to not consume CPU time.  Waiting processes will be granted the lock in
 arrival order.  There is no timeout.
 
+* Relation extension locks.  The relation extension lock manager is
+specialized in relation extensions. In PostgreSQL 11 relation extension
+lock has been moved out of regular lock. It's similar to regular locks
+but doesn't have full dead lock detection, group locking and multiple
+lock modes. When conflicts occur, lock waits are implemented using
+condition variables.
+
 * Regular locks (a/k/a heavyweight locks).  The regular lock manager
 supports a variety of lock modes with table-driven semantics, and it has
 full deadlock detection and automatic release at transaction end.
@@ -40,9 +47,9 @@ 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..788f294
--- /dev/null
+++ b/src/backend/storage/lmgr/extension_lock.c
@@ -0,0 +1,584 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * 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, but don't remove the RELEXTLOCK entry
+ * in the hash table. The all unused entries will be reclaimed when
+ * acquisition once the hash table got full.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/extension_lock.h"
+#include "utils/rel.h"
+
+/*
+ * 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.  Aside from
+ * passing the hashcode to hash_search_with_hash_value(), we can extract
+ * the lock partition number from the hashcode.
+ */
+#define RelExtLockTargetTagHashCode(relextlocktargettag) \
+	get_hash_value(RelExtLockHash, (const void *) relextlocktargettag)
+
+/*
+ * The lockmgr's shared hash tables are partitioned to reduce contention.
+ * To determine which partition a given relid belongs to, compute the tag's
+ * hash code with ExtLockTagHashCode(), then apply one of these macros.
+ * NB: NUM_RELEXTLOCK_PARTITIONS must be a power of 2!
+ */
+#define RelExtLockHashPartition(hashcode) \
+	((hashcode) % NUM_RELEXTLOCK_PARTITIONS)
+#define RelExtLockHashPartitionLock(hashcode) \
+	(&MainLWLockArray[RELEXTLOCK_MANAGER_LWLOCK_OFFSET + \
+					  LockHashPartition(hashcode)].lock)
+#define RelExtLockHashPartitionLockByIndex(i) \
+	(&MainLWLockArray[RELEXTLOCK_MANAGER_LWLOCK_OFFSET + (i)].lock)
+
+#define	RELEXT_VAL_LOCK		((uint32) ((1 << 25)))
+#define RELEXT_LOCK_MASK	((uint32) ((1 << 25)))
+
+/* Must be greater than MAX_BACKENDS - which is 2^23-1, so we're fine. */
+#define RELEXT_PIN_COUNT_MASK	((uint32) ((1 << 24) - 1))
+
+/* FIXME */
+#define N_RELEXTENTS 512
+
+typedef struct RELEXTLOCK
+{
+	/* hash key -- must be first */
+	Oid					relid;
+
+	/* state of exclusive lock and pin counts */
+	pg_atomic_uint32	state;
+
+	ConditionVariable	cv;
+} RELEXTLOCK;
+
+/*
+ * This structure holds information per-object relation extension
+ * lock. held_extlocks represents the RelExtLocks we're holding.
+ * We use this structure to keep track of locked relation extension locks
+ * for release during error recovery.  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
+{
+	RELEXTLOCK		*lock;
+	int				nLocks;
+} relextlock_handle;
+
+static relextlock_handle held_relextlock;
+static int num_held_relextlocks = 0;
+
+static bool RelExtLockAcquire(Oid relid, bool conditional);
+static void RelExtLockRelease(Oid rleid);
+static bool RelExtLockAttemptLock(RELEXTLOCK *extlock);
+static bool RelExtLockShrinkLocks(void);
+
+/*
+ * Pointers to hash tables containing relation extension lock state
+ *
+ * The RelExtLockHash hash table is in shared memory
+ */
+static HTAB *RelExtLockHash;
+
+Size
+RelExtLockShmemSize(void)
+{
+	/* Relation extension lock hash table */
+	return hash_estimate_size(N_RELEXTENTS, sizeof(RELEXTLOCK));
+}
+
+/*
+ * InitRelExtLock
+ *      Initialize the relation extension lock manager's data structures.
+ */
+void
+InitRelExtLocks(void)
+{
+	HASHCTL	info;
+	long		max_table_size = N_RELEXTENTS;
+	long		init_table_size = max_table_size / 2;
+
+	/*
+	 * Allocate hash table for RELEXTLOCK structs. This stores per-relation
+	 * lock.
+	 */
+	MemSet(&info, 0, sizeof(info));
+	info.keysize = sizeof(Oid);
+	info.entrysize = sizeof(RELEXTLOCK);
+	info.num_partitions = NUM_RELEXTLOCK_PARTITIONS;
+
+	RelExtLockHash = ShmemInitHash("RELEXTLOCK Hash",
+								   init_table_size,
+								   max_table_size,
+								   &info,
+								   HASH_ELEM | HASH_BLOBS | HASH_PARTITION);
+}
+
+/*
+ *		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.
+ */
+int
+RelationExtensionLockWaiterCount(Relation relation)
+{
+	LWLock		*partitionLock;
+	RELEXTLOCK	*extlock;
+	Oid			relid;
+	uint32		hashcode;
+	uint32		state;
+	uint32		pin_counts;
+	bool		found;
+
+	relid = RelationGetRelid(relation);
+
+	hashcode = RelExtLockTargetTagHashCode(&relid);
+	partitionLock = RelExtLockHashPartitionLock(hashcode);
+
+	LWLockAcquire(partitionLock, LW_SHARED);
+
+	extlock = (RELEXTLOCK *) hash_search_with_hash_value(RelExtLockHash,
+														 (void *) &relid,
+														 hashcode,
+														 HASH_FIND, &found);
+
+	LWLockRelease(partitionLock);
+
+	/* We assume that we already acquired this lock */
+	Assert(found);
+
+	state = pg_atomic_read_u32(&(extlock->state));
+	pin_counts = state & RELEXT_PIN_COUNT_MASK;
+
+	/* Except for me */
+	return pin_counts - 1;
+}
+
+/*
+ *		UnlockRelationForExtension
+ */
+void
+UnlockRelationForExtension(Relation relation)
+{
+	RelExtLockRelease(relation->rd_id);
+}
+
+/*
+ *		RelationExtensionLockReleaseAll
+ *
+ * release all currently-held relation extension locks
+ */
+void
+RelExtLockReleaseAll(void)
+{
+	if (num_held_relextlocks > 0)
+	{
+		HOLD_INTERRUPTS();
+		RelExtLockRelease(held_relextlock.lock->relid);
+	}
+}
+
+/*
+ *		RelExtLockHoldingLockCount
+ *
+ * Return the number of holding relation extension locks.
+ */
+int
+RelExtLockHoldingLockCount(void)
+{
+	return num_held_relextlocks;
+}
+
+/*
+ *		WaitForRelationExtensionLockToBeFree
+ *
+ * Wait for the relation extension lock on the given relation to
+ * be free without acquiring it.
+ */
+void
+WaitForRelationExtensionLockToBeFree(Relation relation)
+{
+	RELEXTLOCK	*extlock = NULL;
+	Oid		relid;
+	LWLock	*partitionLock;
+	uint32	hashcode;
+	bool	found;
+	bool	pin_counted = false;
+
+	relid = RelationGetRelid(relation);
+
+	/* If the lock is held by me, no need to wait */
+	if (num_held_relextlocks > 0 && relid == held_relextlock.lock->relid)
+		return;
+
+	hashcode = RelExtLockTargetTagHashCode(&relid);
+	partitionLock = RelExtLockHashPartitionLock(hashcode);
+
+	for (;;)
+	{
+		uint32	state;
+
+		LWLockAcquire(partitionLock, LW_SHARED);
+
+		if (!extlock)
+		{
+			extlock = (RELEXTLOCK *) hash_search_with_hash_value(RelExtLockHash,
+																 (void * ) &relid,
+																 hashcode, HASH_FIND,
+																 &found);
+
+			/* Break if there is no RELEXTLOCK entry for this relation */
+			if (!found)
+				break;
+		}
+
+		/*
+		 * Break if nobody is holding the lock on this relation. Before
+		 * leaving, decrement pin count if we had been waiting.
+		 */
+		state = pg_atomic_read_u32(&(extlock)->state);
+		if ((state & RELEXT_LOCK_MASK) == 0)
+		{
+			/* Decrement pin counter */
+			if (pin_counted)
+				state = pg_atomic_sub_fetch_u32(&(extlock->state), 1);
+			break;
+		}
+
+		/* Increment pin count to be waken up by owner */
+		if (!pin_counted)
+		{
+			pg_atomic_add_fetch_u32(&(extlock->state), 1);
+			pin_counted = true;
+		}
+
+		/* Release the partition lock before sleep */
+		LWLockRelease(partitionLock);
+
+		/* Sleep until the lock is released */
+		ConditionVariableSleep(&(extlock->cv), PG_WAIT_RELEXTLOCK);
+	}
+
+	LWLockRelease(partitionLock);
+	ConditionVariableCancelSleep();
+
+	return;
+}
+
+/*
+ * Acquire relation extension lock and create RELEXTLOCK hash entry on shared
+ * hash table. If we're trying to acquire the same lock as what already held,
+ * we just increment nLock locally and return without touching the hash table.
+ */
+static bool
+RelExtLockAcquire(Oid relid, bool conditional)
+{
+	RELEXTLOCK	*extlock = NULL;
+	LWLock	*partitionLock;
+	uint32	hashcode;
+	bool	found;
+	bool	mustwait;
+
+	/*
+	 * If we already hold the lock, we can just increase the count locally.
+	 * Since we don't support dead lock detection for relation extension
+	 * lock and don't control the order of lock acquisition, it cannot not
+	 * happen that trying to take a new lock while holding an another lock.
+	 */
+	if (num_held_relextlocks > 0)
+	{
+		if (relid == held_relextlock.lock->relid)
+		{
+			held_relextlock.nLocks++;
+			return true;
+		}
+		else
+			elog(ERROR,
+				 "cannot acquire relation extension locks for multiple relations at the same");
+	}
+
+	hashcode = RelExtLockTargetTagHashCode(&relid);
+	partitionLock = RelExtLockHashPartitionLock(hashcode);
+
+	for (;;)
+	{
+
+		LWLockAcquire(partitionLock, LW_EXCLUSIVE);
+
+		if (!extlock)
+		{
+			extlock = (RELEXTLOCK *) hash_search_with_hash_value(RelExtLockHash,
+																 (void * ) &relid,
+																 hashcode, HASH_ENTER_NULL,
+																 &found);
+
+			/*
+			 * Failed to create new hash entry. Try to shrink the hash table and
+			 * retry.
+			 */
+			if (!extlock)
+			{
+				bool	ret;
+
+				/*
+				 * Release the partition lock before the shrink since the all partition
+				 * locks are held during the shrink.
+				 */
+				LWLockRelease(partitionLock);
+
+				/* Shrink */
+				ret = RelExtLockShrinkLocks();
+
+				if (!ret)
+					ereport(ERROR,
+							(errmsg("out of shared memory"),
+							 errhint("You might need to increase max_locks_per_transaction.")));
+				continue;
+			}
+
+			/* Not found, initialize */
+			if (!found)
+			{
+				extlock->relid = relid;
+				pg_atomic_init_u32(&(extlock->state), 0);
+				ConditionVariableInit(&(extlock->cv));
+			}
+		}
+
+		/* Increment pin count */
+		pg_atomic_add_fetch_u32(&(extlock->state), 1);
+
+		mustwait = RelExtLockAttemptLock(extlock);
+
+		if (!mustwait)
+			break;	/* got the lock */
+
+		/* Could not got the lock, return iff in conditional locking */
+		if (conditional)
+		{
+			pg_atomic_sub_fetch_u32(&(extlock->state), 1);
+			LWLockRelease(partitionLock);
+			return false;
+		}
+
+		/* Release the partition lock before sleep */
+		LWLockRelease(partitionLock);
+
+		/* Sleep until the lock is released */
+		ConditionVariableSleep(&(extlock->cv), PG_WAIT_RELEXTLOCK);
+	}
+
+	LWLockRelease(partitionLock);
+	ConditionVariableCancelSleep();
+
+	Assert(!mustwait);
+
+	/* Remember lock held by this backend */
+	held_relextlock.lock = extlock;
+	held_relextlock.nLocks = 1;
+	num_held_relextlocks++;
+
+	/* Always return true if not conditional lock */
+	return true;
+}
+
+/*
+ * RelExtLockRelease
+ *
+ * Release a previously acquired relation extension lock. We don't remove
+ * hash entry at the time. Once the hash table got full, all un-pinned hash
+ * entries will be removed.
+ */
+static void
+RelExtLockRelease(Oid relid)
+{
+	RELEXTLOCK	*extlock;
+	LWLock	*partitionLock;
+	uint32	hashcode;
+	uint32	state;
+	uint32	pin_counts;
+	/* Release the lock and decrement the pin counter at a time */
+	uint32	val = RELEXT_VAL_LOCK | 1;
+
+	/* We should have acquired a lock before releasing */
+	Assert(num_held_relextlocks > 0);
+
+	if (relid != held_relextlock.lock->relid)
+		ereport(ERROR,
+				(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+				 errmsg("relation extension lock for %u is not held",
+						relid)));
+
+	/* Decrease the lock count locally */
+	held_relextlock.nLocks--;
+
+	/* If we are still holding the lock, we're done */
+	 if (held_relextlock.nLocks > 0)
+		return;
+
+	hashcode = RelExtLockTargetTagHashCode(&relid);
+	partitionLock = RelExtLockHashPartitionLock(hashcode);
+
+	extlock = held_relextlock.lock;
+
+	/* Keep holding the partition lock until unlocking is done */
+	LWLockAcquire(partitionLock, LW_EXCLUSIVE);
+
+	/* Release the lock and decrement pin counter */
+	state = pg_atomic_sub_fetch_u32(&(extlock->state), val);
+
+	LWLockRelease(partitionLock);
+
+	num_held_relextlocks--;
+
+	/* Wake up waiters if there is someone looking at this lock */
+	pin_counts = state & RELEXT_PIN_COUNT_MASK;
+	if (pin_counts > 0)
+		ConditionVariableBroadcast(&(extlock->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 *extlock)
+{
+	uint32	oldstate;
+
+	oldstate = pg_atomic_read_u32(&extlock->state);
+
+	while (true)
+	{
+		uint32	desired_state;
+		bool	lock_free;
+
+		desired_state = oldstate;
+
+		lock_free = (oldstate & RELEXT_LOCK_MASK) == 0;
+		if (lock_free)
+			desired_state += RELEXT_VAL_LOCK;
+
+		if (pg_atomic_compare_exchange_u32(&extlock->state,
+										   &oldstate, desired_state))
+		{
+			if (lock_free)
+				return false;
+			else
+				return true;
+		}
+	}
+	pg_unreachable();
+}
+
+/*
+ * Reclaim all un-pinned RELEXTLOCK entries from the hash table.
+ */
+static bool
+RelExtLockShrinkLocks(void)
+{
+	HASH_SEQ_STATUS	hstat;
+	RELEXTLOCK		*extlock;
+	List			*entries_to_remove = NIL;
+	ListCell		*cell;
+	int				i;
+
+	/*
+	 * To ensure consistency, take all partition locks in exclusive
+	 * mode.
+	 */
+	for (i = 0; i < NUM_RELEXTLOCK_PARTITIONS; i++)
+		LWLockAcquire(RelExtLockHashPartitionLockByIndex(i), LW_EXCLUSIVE);
+
+	/* Collect all un-pinned RELEXTLOCK entries */
+	hash_seq_init(&hstat, RelExtLockHash);
+	while ((extlock = (RELEXTLOCK *) hash_seq_search(&hstat)) != NULL)
+	{
+		uint32	state = pg_atomic_read_u32(&(extlock->state));
+		uint32	pin_counts = state & RELEXT_PIN_COUNT_MASK;
+
+		if (pin_counts == 0)
+			entries_to_remove = lappend(entries_to_remove, extlock);
+	}
+
+	/* We could not find any entries that we can remove right now */
+	if (list_length(entries_to_remove) == 0)
+		return false;
+
+	/* Remove collected entries from RelExtLockHash has table */
+	foreach (cell, entries_to_remove)
+	{
+		RELEXTLOCK	*el = (RELEXTLOCK *) lfirst(cell);
+		uint32	hc = RelExtLockTargetTagHashCode(&(el->relid));
+
+		hash_search_with_hash_value(RelExtLockHash, (void *) &(el->relid),
+									hc, HASH_REMOVE, NULL);
+	}
+
+	/* Release all partition locks */
+	for (i = 0; i < NUM_RELEXTLOCK_PARTITIONS; i++)
+		LWLockRelease(RelExtLockHashPartitionLockByIndex(i));
+
+	return true;
+}
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..8b642ed 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -40,11 +40,13 @@
 #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"
 #include "storage/spin.h"
 #include "storage/standby.h"
+#include "storage/lmgr.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
 #include "utils/resowner_private.h"
@@ -717,6 +719,15 @@ LockAcquireExtended(const LOCKTAG *locktag,
 	int			status;
 	bool		log_lock = false;
 
+	/*
+	 * We allow to take a relation extension lock after took a
+	 * heavy-weight lock. However, since we don't have dead lock
+	 * detection mechanism between heavy-weight lock and relation
+	 * extension lock it's not allowed taking an another heavy-weight
+	 * lock while holding a relation extension lock.
+	 */
+	Assert(RelExtLockHoldingLockCount() == 0);
+
 	if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
 		elog(ERROR, "unrecognized lock method: %d", lockmethodid);
 	lockMethodTable = LockMethods[lockmethodid];
@@ -3366,6 +3377,7 @@ LockShmemSize(void)
 	/* lock hash table */
 	max_table_size = NLOCKENTS();
 	size = add_size(size, hash_estimate_size(max_table_size, sizeof(LOCK)));
+	size = add_size(size, hash_estimate_size(max_table_size, sizeof(LWLock)));
 
 	/* proclock hash table */
 	max_table_size *= 2;
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e5c3e86..746e263 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pg_trace.h"
 #include "postmaster/postmaster.h"
 #include "replication/slot.h"
+#include "storage/extension_lock.h"
 #include "storage/ipc.h"
 #include "storage/predicate.h"
 #include "storage/proc.h"
@@ -451,6 +452,13 @@ InitializeLWLocks(void)
 	for (id = 0; id < NUM_PREDICATELOCK_PARTITIONS; id++, lock++)
 		LWLockInitialize(&lock->lock, LWTRANCHE_PREDICATE_LOCK_MANAGER);
 
+	/* Initialize relation extension lmgr's LWLocks in main array */
+	lock = MainLWLockArray + NUM_INDIVIDUAL_LWLOCKS +
+		NUM_BUFFER_PARTITIONS + NUM_LOCK_PARTITIONS +
+		NUM_PREDICATELOCK_PARTITIONS;
+	for (id = 0; id < NUM_RELEXTLOCK_PARTITIONS; id++, lock++)
+		LWLockInitialize(&lock->lock, LWTRANCHE_RELEXT_LOCK_MANAGER);
+
 	/* Initialize named tranches. */
 	if (NamedLWLockTrancheRequests > 0)
 	{
@@ -508,6 +516,8 @@ RegisterLWLockTranches(void)
 	LWLockRegisterTranche(LWTRANCHE_LOCK_MANAGER, "lock_manager");
 	LWLockRegisterTranche(LWTRANCHE_PREDICATE_LOCK_MANAGER,
 						  "predicate_lock_manager");
+	LWLockRegisterTranche(LWTRANCHE_RELEXT_LOCK_MANAGER,
+						  "relext_lock_manager");
 	LWLockRegisterTranche(LWTRANCHE_PARALLEL_QUERY_DSA,
 						  "parallel_query_dsa");
 	LWLockRegisterTranche(LWTRANCHE_SESSION_DSA,
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 5f6727d..40ab31a 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 relation extension locks */
+	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..c6c6571 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -735,6 +735,7 @@ typedef enum BackendState
  * ----------
  */
 #define PG_WAIT_LWLOCK				0x01000000U
+#define PG_WAIT_RELEXTLOCK			0x02000000U
 #define PG_WAIT_LOCK				0x03000000U
 #define PG_WAIT_BUFFER_PIN			0x04000000U
 #define PG_WAIT_ACTIVITY			0x05000000U
diff --git a/src/include/storage/extension_lock.h b/src/include/storage/extension_lock.h
new file mode 100644
index 0000000..cbce89b
--- /dev/null
+++ b/src/include/storage/extension_lock.h
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
+
+/* Number of partitions the shared relation extension lock tables are divided into */
+#define LOG2_NUM_RELEXTLOCK_PARTITIONS 4
+#define NUM_RELEXTLOCK_PARTITIONS      (1 << LOG2_NUM_RELEXTLOCK_PARTITIONS)
+
+/* 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 int	RelExtLockHoldingLockCount(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), \
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 596fdad..bef48ea 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -126,8 +126,11 @@ extern PGDLLIMPORT int NamedLWLockTrancheRequests;
 	(BUFFER_MAPPING_LWLOCK_OFFSET + NUM_BUFFER_PARTITIONS)
 #define PREDICATELOCK_MANAGER_LWLOCK_OFFSET \
 	(LOCK_MANAGER_LWLOCK_OFFSET + NUM_LOCK_PARTITIONS)
-#define NUM_FIXED_LWLOCKS \
+#define RELEXTLOCK_MANAGER_LWLOCK_OFFSET \
 	(PREDICATELOCK_MANAGER_LWLOCK_OFFSET + NUM_PREDICATELOCK_PARTITIONS)
+#define NUM_FIXED_LWLOCKS \
+	(PREDICATELOCK_MANAGER_LWLOCK_OFFSET + NUM_PREDICATELOCK_PARTITIONS + \
+	 NUM_RELEXTLOCK_PARTITIONS)
 
 typedef enum LWLockMode
 {
@@ -211,6 +214,7 @@ typedef enum BuiltinTrancheIds
 	LWTRANCHE_BUFFER_MAPPING,
 	LWTRANCHE_LOCK_MANAGER,
 	LWTRANCHE_PREDICATE_LOCK_MANAGER,
+	LWTRANCHE_RELEXT_LOCK_MANAGER,
 	LWTRANCHE_PARALLEL_QUERY_DSA,
 	LWTRANCHE_SESSION_DSA,
 	LWTRANCHE_SESSION_RECORD_TABLE,
