From aa9648afcc0131358b18f0b111515e77af8dfe92 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Wed, 5 Feb 2020 11:47:30 +0900
Subject: [PATCH v14] Move relation extension locks out of heavyweigth lock.

---
 doc/src/sgml/monitoring.sgml              |  16 +-
 src/backend/access/brin/brin_pageops.c    |  10 +-
 src/backend/access/brin/brin_revmap.c     |   7 +-
 src/backend/access/gin/ginutil.c          |   5 +-
 src/backend/access/gin/ginvacuum.c        |   9 +-
 src/backend/access/gist/gistutil.c        |   5 +-
 src/backend/access/gist/gistvacuum.c      |   5 +-
 src/backend/access/heap/hio.c             |  13 +-
 src/backend/access/heap/vacuumlazy.c      |   1 +
 src/backend/access/heap/visibilitymap.c   |   5 +-
 src/backend/access/nbtree/nbtpage.c       |   5 +-
 src/backend/access/nbtree/nbtree.c        |   5 +-
 src/backend/access/spgist/spgutils.c      |   5 +-
 src/backend/access/spgist/spgvacuum.c     |   5 +-
 src/backend/postmaster/pgstat.c           |   3 +
 src/backend/storage/freespace/freespace.c |   5 +-
 src/backend/storage/ipc/ipci.c            |   7 +
 src/backend/storage/lmgr/Makefile         |   1 +
 src/backend/storage/lmgr/README           |  16 +-
 src/backend/storage/lmgr/extension_lock.c | 469 ++++++++++++++++++++++
 src/backend/storage/lmgr/lmgr.c           |  78 ----
 src/backend/storage/lmgr/lock.c           |   8 +
 src/backend/storage/lmgr/proc.c           |   3 +
 src/backend/utils/adt/lockfuncs.c         |   2 -
 src/include/pgstat.h                      |   1 +
 src/include/storage/extension_lock.h      |  38 ++
 src/include/storage/lmgr.h                |   7 -
 src/include/storage/lock.h                |  10 -
 src/tools/pgindent/typedefs.list          |   1 +
 29 files changed, 600 insertions(+), 145 deletions(-)
 create mode 100644 src/backend/storage/lmgr/extension_lock.c
 create mode 100644 src/include/storage/extension_lock.h

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 8839699079..13283e6d67 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -714,8 +714,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>
@@ -1178,14 +1178,10 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          counters during Parallel Hash plan 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>
@@ -1319,7 +1315,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="36"><literal>IPC</literal></entry>
+         <entry morerows="37"><literal>IPC</literal></entry>
          <entry><literal>BgWorkerShutdown</literal></entry>
          <entry>Waiting for background worker to shut down.</entry>
         </row>
@@ -1451,6 +1447,10 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>Promote</literal></entry>
          <entry>Waiting for standby promotion.</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>
diff --git a/src/backend/access/brin/brin_pageops.c b/src/backend/access/brin/brin_pageops.c
index 87de0b855b..f62c9e2fe3 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"
@@ -633,8 +634,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))
@@ -728,7 +728,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);
@@ -776,7 +776,7 @@ brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size itemsz,
 					brin_initialize_empty_new_buffer(irel, buf);
 
 				if (extensionLockHeld)
-					UnlockRelationForExtension(irel, ExclusiveLock);
+					UnlockRelationForExtension(irel);
 
 				ReleaseBuffer(buf);
 
@@ -794,7 +794,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 9c4b3e2202..75dd1b805d 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"
 
@@ -571,7 +572,7 @@ revmap_physical_extend(BrinRevmap *revmap)
 	else
 	{
 		if (needLock)
-			LockRelationForExtension(irel, ExclusiveLock);
+			LockRelationForExtension(irel);
 
 		buf = ReadBuffer(irel, P_NEW);
 		if (BufferGetBlockNumber(buf) != mapBlk)
@@ -583,7 +584,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;
@@ -592,7 +593,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 a7e55caf28..59f1323e1a 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -22,6 +22,7 @@
 #include "catalog/pg_type.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
@@ -327,13 +328,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 260cedff88..64e82434c4 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 "storage/predicate.h"
@@ -733,10 +734,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;
 
@@ -783,10 +784,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 dd975b164c..c9c6854323 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,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/float.h"
@@ -866,13 +867,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 a9c616c772..34e8759632 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -20,6 +20,7 @@
 #include "commands/vacuum.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/memutils.h"
@@ -195,10 +196,10 @@ gistvacuumscan(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/heap/hio.c b/src/backend/access/heap/hio.c
index aa3f14c019..5df754aa21 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"
@@ -191,7 +192,7 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
 	int			lockWaiters;
 
 	/* Use the length of the lock wait queue to judge how much to extend. */
-	lockWaiters = RelationExtensionLockWaiterCount(relation);
+	lockWaiters = EstimateNumberOfExtensionLockWaiters(relation);
 	if (lockWaiters <= 0)
 		return;
 
@@ -555,11 +556,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
@@ -573,7 +574,7 @@ loop:
 			 */
 			if (targetBlock != InvalidBlockNumber)
 			{
-				UnlockRelationForExtension(relation, ExclusiveLock);
+				UnlockRelationForExtension(relation);
 				goto loop;
 			}
 
@@ -613,7 +614,7 @@ loop:
 	 * the relation some more.
 	 */
 	if (needLock)
-		UnlockRelationForExtension(relation, ExclusiveLock);
+		UnlockRelationForExtension(relation);
 
 	/*
 	 * Lock the other buffer. It's guaranteed to be of a lower page number
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 8ce501151e..d448c448aa 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -71,6 +71,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 "tcop/tcopprot.h"
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index 0a51678c40..e4389b6961 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -92,6 +92,7 @@
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
 #include "storage/bufmgr.h"
+#include "storage/extension_lock.h"
 #include "storage/lmgr.h"
 #include "storage/smgr.h"
 #include "utils/inval.h"
@@ -632,7 +633,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);
@@ -670,5 +671,5 @@ 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);
 }
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index f05cbe7467..defa9ad163 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"
@@ -850,7 +851,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);
 
@@ -864,7 +865,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 5254bc7ef5..ecf3ecef64 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -29,6 +29,7 @@
 #include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "storage/condition_variable.h"
+#include "storage/extension_lock.h"
 #include "storage/indexfsm.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
@@ -1017,10 +1018,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);
 
 		if (info->report_progress)
 			pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_TOTAL,
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 4924ae1c59..a57845d615 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -23,6 +23,7 @@
 #include "access/xact.h"
 #include "catalog/pg_amop.h"
 #include "commands/vacuum.h"
+#include "storage/extension_lock.h"
 #include "storage/bufmgr.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
@@ -258,13 +259,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 bd98707f3c..f6c1156bf9 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/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 7169509a79..81acdf3a26 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3836,6 +3836,9 @@ pgstat_get_wait_ipc(WaitEventIPC w)
 		case WAIT_EVENT_PROMOTE:
 			event_name = "Promote";
 			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 a5083db02b..6e363adecc 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"
@@ -613,7 +614,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);
@@ -641,7 +642,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);
 }
 
 /*
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 427b0d59cd..6b76301af9 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -35,6 +35,7 @@
 #include "replication/walsender.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"
@@ -131,6 +132,7 @@ CreateSharedMemoryAndSemaphores(void)
 		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());
@@ -228,6 +230,11 @@ CreateSharedMemoryAndSemaphores(void)
 	 */
 	InitPredicateLocks();
 
+	/*
+	 * Set up relation extension lock manager
+	 */
+	InitRelExtLocks();
+
 	/*
 	 * Set up process table
 	 */
diff --git a/src/backend/storage/lmgr/Makefile b/src/backend/storage/lmgr/Makefile
index 829b792fcb..1aed82703c 100644
--- a/src/backend/storage/lmgr/Makefile
+++ b/src/backend/storage/lmgr/Makefile
@@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = \
 	condition_variable.o \
 	deadlock.o \
+	extension_lock.o \
 	lmgr.o \
 	lock.o \
 	lwlock.o \
diff --git a/src/backend/storage/lmgr/README b/src/backend/storage/lmgr/README
index 56b0a12a3e..960d1f3f09 100644
--- a/src/backend/storage/lmgr/README
+++ b/src/backend/storage/lmgr/README
@@ -3,7 +3,7 @@ src/backend/storage/lmgr/README
 Locking Overview
 ================
 
-Postgres uses four types of interprocess locks:
+Postgres uses five types of interprocess locks:
 
 * Spinlocks.  These are intended for *very* short-term locks.  If a lock
 is to be held more than a few dozen instructions, or across any sort of
@@ -36,13 +36,21 @@ Regular locks should be used for all user-driven lock requests.
 
 * SIReadLock predicate locks.  See separate README-SSI file for details.
 
+* Relation extension locks. Only one process can extend a relation at
+a time; we use a specialized lock manager for this purpose, which is
+much simpler than the regular lock manager.  It is similar to the
+lightweight lock mechanism, but is ever simpler because there is only
+one lock mode and only one lock can be taken at a time. A process holding
+a relation extension lock is interruptible, unlike a process holding an
+LWLock.
+
 Acquisition of either a spinlock or a lightweight lock causes query
 cancel and die() interrupts to be held off until all such locks are
 released. No such restriction exists for regular locks, however.  Also
 note that we can accept query cancel and die() interrupts while waiting
-for a regular lock, but we will not accept them while waiting for
-spinlocks or LW locks. It is therefore not a good idea to use LW locks
-when the wait time might exceed a few seconds.
+for a relation extension lock or a regular lock, but we will not accept
+them while waiting for spinlocks or LW locks. It is therefore not a good
+idea to use LW locks when the wait time might exceed a few seconds.
 
 The rest of this README file discusses the regular lock manager in detail.
 
diff --git a/src/backend/storage/lmgr/extension_lock.c b/src/backend/storage/lmgr/extension_lock.c
new file mode 100644
index 0000000000..5a3bf5ea59
--- /dev/null
+++ b/src/backend/storage/lmgr/extension_lock.c
@@ -0,0 +1,469 @@
+/*-------------------------------------------------------------------------
+ *
+ * extension_lock.c
+ *	  Relation extension lock manager
+ *
+ * This specialized lock manager is used only for relation extension
+ * locks.  Unlike the heavyweight lock manager, it doesn't provide
+ * deadlock detection or group locking.  Unlike lwlock.c, extension lock
+ * waits are interruptible.  Unlike both systems, there is only one lock
+ * mode.
+ *
+ * False sharing is possible.  We have a fixed-size array of locks, and
+ * every database OID/relation OID combination is mapped to a slot in
+ * the array.  Therefore, if two processes try to extend relations that
+ * map to the same array slot, they will contend even though it would
+ * be OK to let both proceed at once.  Since these locks are typically
+ * taken only for very short periods of time, this doesn't seem likely
+ * to be a big problem in practice.  If it is, we could make the array
+ * bigger.
+ *
+ * The extension lock manager is much faster than the regular heavyweight
+ * lock manager.  The lack of group locking is a feature, not a bug,
+ * because while cooperating backends can all (for example) access a
+ * relation on which they jointly hold AccessExclusiveLock at the same time,
+ * it's not safe for them to extend the relation at the same time.
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *		src/backend/storage/lmgr/extension_lock.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include "catalog/catalog.h"
+#include "postmaster/postmaster.h"
+#include "storage/extension_lock.h"
+#include "utils/rel.h"
+
+#define N_RELEXTLOCK_ENTS		1024
+
+/*
+ * We can't use bit 31 as the lock bit because pg_atomic_sub_fetch_u32 can't
+ * handle an attempt to subtract INT_MIN.
+ */
+#define RELEXT_LOCK_BIT			((uint32) 1 << 30)
+#define RELEXT_WAIT_COUNT_MASK	(RELEXT_LOCK_BIT - 1)
+
+typedef struct RelExtLockTag
+{
+	Oid			dbid;			/* InvalidOid for a shared relation */
+	Oid			relid;
+} RelExtLockTag;
+
+typedef struct RelExtLock
+{
+	pg_atomic_uint32 state;
+	ConditionVariable cv;
+} RelExtLock;
+
+/*
+ * Backend-private state for relation extension locks.  "relid" is the last
+ * relation whose RelExtLock we looked up, and "lock" is a pointer to the
+ * RelExtLock to which it mapped.  This speeds up the fairly common case where
+ * we acquire the same relation extension lock repeatedly.  nLocks is 0 is the
+ * number of times we've acquired that lock; 0 means we don't hold it, while
+ * any value >0 means we do.
+ *
+ * A backend can't hold more than one relation extension lock at the same
+ * time, although it can hold the same lock more than once.  Sometimes we try
+ * to acquire a lock for additional forks while already holding the lock for
+ * the main fork; for example, this might happen when adding extra relation
+ * blocks for both relation and its free space map. But since this lock
+ * manager doesn't distinguish between the forks, we just increment nLocks in
+ * the case.
+ */
+typedef struct relextlock_handle
+{
+	Oid			relid;
+	RelExtLock *lock;
+	int			nLocks;			/* > 0 means holding it */
+	bool		waiting;		/* true if we're waiting it */
+} relextlock_handle;
+
+static relextlock_handle held_relextlock;
+static RelExtLock *RelExtLockArray;
+
+static bool RelExtLockAcquire(Oid relid, bool conditional);
+static bool RelExtLockAttemptLock(RelExtLock *relextlock);
+static void RelExtLockRelease(void);
+static inline uint32 RelExtLockTargetTagToIndex(RelExtLockTag *locktag);
+
+/*
+ * Estimate space required for a fixed-size array of RelExtLock structures.
+ */
+Size
+RelExtLockShmemSize(void)
+{
+	return mul_size(N_RELEXTLOCK_ENTS, sizeof(RelExtLock));
+}
+
+/*
+ * Initialize extension lock manager.
+ */
+void
+InitRelExtLocks(void)
+{
+	bool		found;
+	int			i;
+
+	/* Verify that we have enough bits for maximum possible waiter count. */
+	StaticAssertStmt(RELEXT_WAIT_COUNT_MASK >= MAX_BACKENDS,
+					 "maximum waiter count of relation extension lock exceeds MAX_BACKENDS");
+
+	RelExtLockArray = (RelExtLock *)
+		ShmemInitStruct("Relation Extension Lock",
+						N_RELEXTLOCK_ENTS * sizeof(RelExtLock),
+						&found);
+
+	/* we're the first - initialize */
+	if (!found)
+	{
+		for (i = 0; i < N_RELEXTLOCK_ENTS; i++)
+		{
+			RelExtLock *relextlock = &RelExtLockArray[i];
+
+			pg_atomic_init_u32(&(relextlock->state), 0);
+			ConditionVariableInit(&(relextlock->cv));
+		}
+	}
+}
+
+/*
+ * This lock is used to interlock addition of pages to relations.
+ * We need such locking because bufmgr/smgr definition of P_NEW is not
+ * race-condition-proof.
+ *
+ * We assume the caller is already holding some type of regular lock on
+ * the relation, so no AcceptInvalidationMessages call is needed here.
+ */
+void
+LockRelationForExtension(Relation relation)
+{
+	RelExtLockAcquire(RelationGetRelid(relation), false);
+}
+
+/*
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE iff the lock was acquired.
+ */
+bool
+ConditionalLockRelationForExtension(Relation relation)
+{
+	return RelExtLockAcquire(RelationGetRelid(relation), true);
+}
+
+/*
+ * Estimate the number of processes waiting for the given relation extension
+ * lock. Note that since multiple relations hash to the same RelExtLock entry,
+ * the return value might be inflated.
+ */
+int
+EstimateNumberOfExtensionLockWaiters(Relation relation)
+{
+	RelExtLockTag tag;
+	RelExtLock *relextlock;
+	uint32		state;
+	Oid			relid = RelationGetRelid(relation);
+
+	/* Make a lock tag */
+	tag.dbid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId;
+	tag.relid = relid;
+
+	relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+	state = pg_atomic_read_u32(&(relextlock->state));
+
+	return (state & RELEXT_WAIT_COUNT_MASK);
+}
+
+/*
+ * Release a previously-acquired extension lock.
+ */
+void
+UnlockRelationForExtension(Relation relation)
+{
+	Oid			relid = RelationGetRelid(relation);
+
+	if (held_relextlock.nLocks <= 0 || relid != held_relextlock.relid)
+	{
+		elog(WARNING,
+			 "relation extension lock for %u is not held",
+			 relid);
+		return;
+	}
+
+	/*
+	 * If we acquired it multiple times, only change shared state when we have
+	 * released it as many times as we acquired it.
+	 */
+	if (--held_relextlock.nLocks == 0)
+		RelExtLockRelease();
+}
+
+/*
+ * Release any extension lock held, and any wait count for an extension lock.
+ * This is intended to be invoked during error cleanup.
+ */
+void
+RelExtLockCleanup(void)
+{
+	if (held_relextlock.nLocks > 0)
+	{
+		/* Release the lock even if we acquired it multiple times. */
+		held_relextlock.nLocks = 0;
+		RelExtLockRelease();
+		Assert(!held_relextlock.waiting);
+	}
+	else if (held_relextlock.waiting)
+	{
+		/* We were waiting for the lock; release the wait count we held. */
+		held_relextlock.waiting = false;
+		pg_atomic_sub_fetch_u32(&(held_relextlock.lock->state), 1);
+	}
+}
+
+/*
+ * Are we holding any extension lock?
+ */
+bool
+IsAnyRelationExtensionLockHeld(void)
+{
+	return held_relextlock.nLocks > 0;
+}
+
+/*
+ *		WaitForRelationExtensionLockToBeFree
+ *
+ * Wait for the relation extension lock on the given relation to
+ * be free without acquiring it.
+ */
+void
+WaitForRelationExtensionLockToBeFree(Relation relation)
+{
+	RelExtLock *relextlock;
+	Oid			relid;
+
+	relid = RelationGetRelid(relation);
+
+	if (held_relextlock.nLocks > 0)
+	{
+		/*
+		 * If we already hold the lock, nobody else does, so we can return
+		 * immediately.
+		 */
+		if (relid == held_relextlock.relid)
+			return;
+		elog(ERROR,
+			 "can only manipulate one relation extension lock at a time");
+	}
+
+	/*
+	 * If the last relation extension lock we touched is the same one for
+	 * which we now need to wait, we can use our cached pointer to the lock
+	 * instead of recomputing it.
+	 */
+	if (relid == held_relextlock.relid)
+		relextlock = held_relextlock.lock;
+	else
+	{
+		RelExtLockTag tag;
+
+		tag.dbid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId;
+		tag.relid = relid;
+		relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+		held_relextlock.relid = relid;
+		held_relextlock.lock = relextlock;
+	}
+
+	for (;;)
+	{
+		uint32		state;
+
+		state = pg_atomic_read_u32(&(relextlock)->state);
+
+		/* Break if nobody is holding the lock on this relation */
+		if ((state & RELEXT_LOCK_BIT) == 0)
+			break;
+
+		/* Could not get the lock, prepare to wait */
+		if (!held_relextlock.waiting)
+		{
+			pg_atomic_add_fetch_u32(&(relextlock->state), 1);
+			held_relextlock.waiting = true;
+		}
+
+		/* Sleep until something happens, then recheck */
+		ConditionVariableSleep(&(relextlock->cv),
+							   WAIT_EVENT_RELATION_EXTENSION_LOCK);
+	}
+
+	ConditionVariableCancelSleep();
+
+	/* Release any wait count we hold */
+	if (held_relextlock.waiting)
+	{
+		pg_atomic_sub_fetch_u32(&(relextlock->state), 1);
+		held_relextlock.waiting = false;
+	}
+}
+
+/*
+ * Compute the hash code associated with a RelExtLock.
+ */
+static inline uint32
+RelExtLockTargetTagToIndex(RelExtLockTag *locktag)
+{
+	return tag_hash(locktag, sizeof(RelExtLockTag)) % N_RELEXTLOCK_ENTS;
+}
+
+/*
+ * Acquire a relation extension lock.
+ */
+static bool
+RelExtLockAcquire(Oid relid, bool conditional)
+{
+	RelExtLock *relextlock;
+	bool		mustwait;
+
+	/*
+	 * If we already hold the lock, we can just increase the count locally.
+	 * Since we don't do deadlock detection, caller must not try to take a new
+	 * relation extension lock while already holding them.
+	 */
+	if (held_relextlock.nLocks > 0)
+	{
+		if (relid != held_relextlock.relid)
+			elog(ERROR,
+				 "can only acquire one relation extension lock at a time");
+
+		held_relextlock.nLocks++;
+		return true;
+	}
+
+	/*
+	 * If the last relation extension lock we touched is the same one for we
+	 * now need to acquire, we can use our cached pointer to the lock instead
+	 * of recomputing it.  This is likely to be a common case in practice.
+	 */
+	if (relid == held_relextlock.relid)
+		relextlock = held_relextlock.lock;
+	else
+	{
+		RelExtLockTag tag;
+
+		/* Make a lock tag */
+		tag.dbid = IsSharedRelation(relid) ? InvalidOid : MyDatabaseId;
+		tag.relid = relid;
+
+		relextlock = &RelExtLockArray[RelExtLockTargetTagToIndex(&tag)];
+
+		/* Remember the lock we're interested in */
+		held_relextlock.relid = relid;
+		held_relextlock.lock = relextlock;
+	}
+
+	held_relextlock.waiting = false;
+	for (;;)
+	{
+		mustwait = RelExtLockAttemptLock(relextlock);
+
+		if (!mustwait)
+			break;				/* got the lock */
+
+		/* Could not got the lock, return iff in locking conditionally */
+		if (conditional)
+			return false;
+
+		/* Could not get the lock, prepare to wait */
+		if (!held_relextlock.waiting)
+		{
+			pg_atomic_add_fetch_u32(&(relextlock->state), 1);
+			held_relextlock.waiting = true;
+		}
+
+		/* Sleep until something happens, then recheck */
+		ConditionVariableSleep(&(relextlock->cv),
+							   WAIT_EVENT_RELATION_EXTENSION_LOCK);
+	}
+
+	ConditionVariableCancelSleep();
+
+	/* Release any wait count we hold */
+	if (held_relextlock.waiting)
+	{
+		pg_atomic_sub_fetch_u32(&(relextlock->state), 1);
+		held_relextlock.waiting = false;
+	}
+
+	Assert(!mustwait);
+
+	/* Remember lock held by this backend */
+	held_relextlock.relid = relid;
+	held_relextlock.lock = relextlock;
+	held_relextlock.nLocks = 1;
+
+	/* We got the lock! */
+	return true;
+}
+
+/*
+ * Attempt to atomically acquire the relation extension lock.
+ *
+ * Returns true if the lock isn't free and we need to wait.
+ */
+static bool
+RelExtLockAttemptLock(RelExtLock *relextlock)
+{
+	uint32		oldstate;
+
+	oldstate = pg_atomic_read_u32(&relextlock->state);
+
+	while (true)
+	{
+		bool		lock_free;
+
+		lock_free = (oldstate & RELEXT_LOCK_BIT) == 0;
+
+		if (!lock_free)
+			return true;
+
+		if (pg_atomic_compare_exchange_u32(&relextlock->state,
+										   &oldstate,
+										   oldstate | RELEXT_LOCK_BIT))
+			return false;
+	}
+
+	pg_unreachable();
+}
+
+/*
+ * Release extension lock in shared memory.  Should be called when our local
+ * lock count drops to 0.
+ */
+static void
+RelExtLockRelease(void)
+{
+	RelExtLock *relextlock;
+	uint32		state;
+	uint32		wait_counts;
+
+	Assert(held_relextlock.nLocks == 0);
+
+	relextlock = held_relextlock.lock;
+
+	/* Release the lock */
+	state = pg_atomic_sub_fetch_u32(&(relextlock->state), RELEXT_LOCK_BIT);
+
+	/* If there may be waiters, wake them up */
+	wait_counts = state & RELEXT_WAIT_COUNT_MASK;
+
+	if (wait_counts > 0)
+		ConditionVariableBroadcast(&(relextlock->cv));
+}
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index 2010320095..174e0b051c 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -388,78 +388,6 @@ UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
 	LockRelease(&tag, lockmode, true);
 }
 
-/*
- *		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
  *
@@ -1092,12 +1020,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 56dba09299..1bd970a907 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"
@@ -749,6 +750,13 @@ LockAcquireExtended(const LOCKTAG *locktag,
 	bool		found_conflict;
 	bool		log_lock = false;
 
+	/*
+	 * Relation extension locks don't participate in deadlock detection,
+	 * so make sure we don't try to acquire a heavyweight lock while
+	 * holding one.
+	 */
+	Assert(!IsAnyRelationExtensionLockHeld());
+
 	if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
 		elog(ERROR, "unrecognized lock method: %d", lockmethodid);
 	lockMethodTable = LockMethods[lockmethodid];
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 32df8c85a1..883cc0d4cc 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -45,6 +45,7 @@
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "storage/condition_variable.h"
+#include "storage/extension_lock.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
 #include "storage/pmsignal.h"
@@ -790,6 +791,8 @@ ProcReleaseLocks(bool isCommit)
 		return;
 	/* If waiting, get off wait queue (should only be needed after error) */
 	LockErrorCleanup();
+	/* Release any relation extension lock or wait counts */
+	RelExtLockCleanup();
 	/* Release standard locks, including session-level if aborting */
 	LockReleaseAll(DEFAULT_LOCKMETHOD, !isCommit);
 	/* Release transaction-level advisory locks */
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 7e47ebeb6f..86d961dc31 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",
@@ -240,7 +239,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 aecb6013f0..12e6240825 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -848,6 +848,7 @@ typedef enum
 	WAIT_EVENT_PARALLEL_BITMAP_SCAN,
 	WAIT_EVENT_PARALLEL_CREATE_INDEX_SCAN,
 	WAIT_EVENT_PARALLEL_FINISH,
+	WAIT_EVENT_RELATION_EXTENSION_LOCK,
 	WAIT_EVENT_PROCARRAY_GROUP_UPDATE,
 	WAIT_EVENT_PROMOTE,
 	WAIT_EVENT_REPLICATION_ORIGIN_DROP,
diff --git a/src/include/storage/extension_lock.h b/src/include/storage/extension_lock.h
new file mode 100644
index 0000000000..0b26fa5716
--- /dev/null
+++ b/src/include/storage/extension_lock.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * extension_lock.h
+ *	  Relation extension lock manager
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/extension_lock.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef EXTENSION_LOCK_H
+#define EXTENSION_LOCK_H
+
+#ifdef FRONTEND
+#error "extension_lock.h may not be included from frontend code"
+#endif
+
+#include "port/atomics.h"
+#include "storage/s_lock.h"
+#include "storage/condition_variable.h"
+#include "storage/proclist_types.h"
+
+/* Lock a relation for extension */
+extern Size RelExtLockShmemSize(void);
+extern void InitRelExtLocks(void);
+extern void LockRelationForExtension(Relation relation);
+extern void UnlockRelationForExtension(Relation relation);
+extern bool ConditionalLockRelationForExtension(Relation relation);
+extern int	EstimateNumberOfExtensionLockWaiters(Relation relation);
+extern void WaitForRelationExtensionLockToBeFree(Relation relation);
+extern void RelExtLockCleanup(void);
+extern bool	IsAnyRelationExtensionLockHeld(void);
+
+#endif	/* EXTENSION_LOCK_H */
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index 3acc11aa5a..7609c1a58c 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -52,13 +52,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 bb8e4e6e5b..be17db8c55 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -137,7 +137,6 @@ typedef uint16 LOCKMETHODID;
 typedef enum LockTagType
 {
 	LOCKTAG_RELATION,			/* whole relation */
-	LOCKTAG_RELATION_EXTEND,	/* the right to extend a relation */
 	LOCKTAG_PAGE,				/* one page of a relation */
 	LOCKTAG_TUPLE,				/* one physical tuple */
 	LOCKTAG_TRANSACTION,		/* transaction (for waiting for xact done) */
@@ -185,15 +184,6 @@ typedef struct LOCKTAG
 	 (locktag).locktag_type = LOCKTAG_RELATION, \
 	 (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
 
-/* same ID info as RELATION */
-#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)
-
 /* ID info for a page is RELATION info + BlockNumber */
 #define SET_LOCKTAG_PAGE(locktag,dboid,reloid,blocknum) \
 	((locktag).locktag_field1 = (dboid), \
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e216de9570..aa3da09e9b 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3181,6 +3181,7 @@ registered_buffer
 regmatch_t
 regoff_t
 regproc
+relextlock_handle
 relopt_bool
 relopt_gen
 relopt_int
-- 
2.23.0

