diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index e2630fd368..44ce5a045a 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -376,6 +376,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_checksum</structname><indexterm><primary>pg_stat_checksum</primary></indexterm></entry>
+      <entry>One row per database, plus one for the shared objects, showing
+      database-wide checksums statistics. See
+       <xref linkend="pg-stat-checksum-view"/> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_database</structname><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
@@ -2397,6 +2405,59 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-checksum-view" xreflabel="pg_stat_checksum">
+   <title><structname>pg_stat_checksum</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>datid</structfield></entry>
+     <entry><type>oid</type></entry>
+     <entry>OID of a database, or 0 for objects belonging to a shared relation</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</structfield></entry>
+     <entry><type>name</type></entry>
+     <entry>Name of this database, or <literal>&lt;shared_objects&gt;</literal></entry>
+    </row>
+    <row>
+     <entry><structfield>checksum_checks</structfield></entry>
+     <entry><type>bigint</type></entry>
+     <entry>Number of data page checksum checks processed in this database</entry>
+    </row>
+    <row>
+     <entry><structfield>checksum_failures</structfield></entry>
+     <entry><type>bigint</type></entry>
+     <entry>Number of data page checksum failures detected in this
+     database</entry>
+    </row>
+    <row>
+     <entry><structfield>checksum_last_failure</structfield></entry>
+     <entry><type>timestamp with time zone</type></entry>
+     <entry>Time at which the last data page checksum failures was detected in
+     this database</entry>
+    </row>
+    <row>
+     <entry><structfield>stats_reset</structfield></entry>
+     <entry><type>timestamp with time zone</type></entry>
+     <entry>Time at which these statistics were last reset</entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_database</structname> view will contain one row
+   for each database in the cluster, showing database-wide statistics.
+  </para>
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -2508,11 +2569,6 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
      <entry><type>bigint</type></entry>
      <entry>Number of deadlocks detected in this database</entry>
     </row>
-    <row>
-     <entry><structfield>checksum_failures</structfield></entry>
-     <entry><type>bigint</type></entry>
-     <entry>Number of data page checksum failures detected in this database</entry>
-    </row>
     <row>
      <entry><structfield>blk_read_time</structfield></entry>
      <entry><type>double precision</type></entry>
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 84fb37c293..7d4fed9a91 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -218,7 +218,9 @@ PostgreSQL documentation
         I/O system that would otherwise be silent. Enabling checksums
         may incur a noticeable performance penalty. This option can only
         be set during initialization, and cannot be changed later. If
-        set, checksums are calculated for all objects, in all databases.
+        set, checksums are calculated for all objects, in all databases. All
+        checksum activity  will be reported in the <xref
+        linkend="pg-stat-checksum-view"/> view.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index c4f3950e5b..82a2f8999d 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -531,7 +531,8 @@ PostgreSQL documentation
         By default, checksums are verified and checksum failures will result
         in a non-zero exit status. However, the base backup will not be
         removed in such a case, as if the <option>--no-clean</option> option
-        had been used.
+        had been used.  Checksum verifications will also be reported in the
+        <xref linkend="pg-stat-checksum-view"/> view.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/ref/pg_checksums.sgml b/doc/src/sgml/ref/pg_checksums.sgml
index 6a47dda683..c207d7db0c 100644
--- a/doc/src/sgml/ref/pg_checksums.sgml
+++ b/doc/src/sgml/ref/pg_checksums.sgml
@@ -38,8 +38,10 @@ PostgreSQL documentation
   <para>
    <application>pg_checksums</application> verifies data checksums in a
    <productname>PostgreSQL</productname> cluster.  The server must be shut
-   down cleanly before running <application>pg_checksums</application>.
-   The exit status is zero if there are no checksum errors, otherwise nonzero.
+   down cleanly before running <application>pg_checksums</application>.  As a
+   consequence, the <structname>pg_stat_checksum</structname> view won't
+   reflect this activity.  The exit status is zero if there are no checksum
+   errors, otherwise nonzero.
   </para>
  </refsect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 7723f01327..68acc21604 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -823,12 +823,25 @@ CREATE VIEW pg_stat_database AS
             pg_stat_get_db_temp_files(D.oid) AS temp_files,
             pg_stat_get_db_temp_bytes(D.oid) AS temp_bytes,
             pg_stat_get_db_deadlocks(D.oid) AS deadlocks,
-            pg_stat_get_db_checksum_failures(D.oid) AS checksum_failures,
             pg_stat_get_db_blk_read_time(D.oid) AS blk_read_time,
             pg_stat_get_db_blk_write_time(D.oid) AS blk_write_time,
             pg_stat_get_db_stat_reset_time(D.oid) AS stats_reset
     FROM pg_database D;
 
+CREATE VIEW pg_stat_checksum AS
+    SELECT
+            D.oid AS datid,
+            D.datname AS datname,
+            pg_stat_get_db_checksum_checks(D.oid) AS checksum_checks,
+            pg_stat_get_db_checksum_failures(D.oid) AS checksum_failures,
+            pg_stat_get_db_checksum_last_failure(D.oid) AS checksum_last_failure,
+            pg_stat_get_db_stat_reset_time(D.oid) AS stats_reset
+    FROM (
+        SELECT oid, datname FROM pg_database
+        UNION ALL
+        SELECT 0, '<shared objects>'
+    ) D;
+
 CREATE VIEW pg_stat_database_conflicts AS
     SELECT
             D.oid AS datid,
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index ba31f532ea..a18bd00dfc 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -335,7 +335,7 @@ static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len);
 static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
-static void pgstat_recv_checksum_failure(PgStat_MsgChecksumFailure *msg, int len);
+static void pgstat_recv_checksum(PgStat_MsgChecksum *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
 
 /* ------------------------------------------------------------
@@ -1523,35 +1523,42 @@ pgstat_report_deadlock(void)
 
 
 /* --------
- * pgstat_report_checksum_failures_in_db(dboid, failure_count) -
+ * pgstat_report_checksum_in_db(dboid, failure_count) -
  *
- *	Tell the collector about one or more checksum failures.
+ *	Tell the collector about one or more checksum event.
  * --------
  */
 void
-pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount)
+pgstat_report_checksum_in_db(Oid dboid, int checkcount, int failurecount)
 {
-	PgStat_MsgChecksumFailure msg;
+	PgStat_MsgChecksum msg;
 
 	if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
 		return;
 
-	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_CHECKSUMFAILURE);
+	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_CHECKSUM);
 	msg.m_databaseid = dboid;
+	msg.m_checkcount = checkcount;
 	msg.m_failurecount = failurecount;
+
+	if (failurecount > 0)
+		msg.m_failure_time = GetCurrentTimestamp();
+	else
+		msg.m_failure_time = 0;
+
 	pgstat_send(&msg, sizeof(msg));
 }
 
 /* --------
- * pgstat_report_checksum_failure() -
+ * pgstat_report_checksum() -
  *
- *	Tell the collector about a checksum failure.
+ *	Tell the collector about a checksum verification.
  * --------
  */
 void
-pgstat_report_checksum_failure(void)
+pgstat_report_checksum(bool failed)
 {
-	pgstat_report_checksum_failures_in_db(MyDatabaseId, 1);
+	pgstat_report_checksum_in_db(MyDatabaseId, 1, failed ? 1 : 0);
 }
 
 /* --------
@@ -4491,8 +4498,8 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
-				case PGSTAT_MTYPE_CHECKSUMFAILURE:
-					pgstat_recv_checksum_failure((PgStat_MsgChecksumFailure *) &msg, len);
+				case PGSTAT_MTYPE_CHECKSUM:
+					pgstat_recv_checksum((PgStat_MsgChecksum *) &msg, len);
 					break;
 
 				default:
@@ -4594,7 +4601,9 @@ reset_dbentry_counters(PgStat_StatDBEntry *dbentry)
 	dbentry->n_temp_files = 0;
 	dbentry->n_temp_bytes = 0;
 	dbentry->n_deadlocks = 0;
+	dbentry->n_checksum_checks = 0;
 	dbentry->n_checksum_failures = 0;
+	dbentry->last_checksum_failure = 0;
 	dbentry->n_block_read_time = 0;
 	dbentry->n_block_write_time = 0;
 
@@ -6240,17 +6249,24 @@ pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len)
 /* ----------
  * pgstat_recv_checksum_failure() -
  *
- *	Process a CHECKSUMFAILURE message.
+ *	Process a CHECKSUM message.
  * ----------
  */
 static void
-pgstat_recv_checksum_failure(PgStat_MsgChecksumFailure *msg, int len)
+pgstat_recv_checksum(PgStat_MsgChecksum *msg, int len)
 {
 	PgStat_StatDBEntry *dbentry;
 
 	dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
 
+	dbentry->n_checksum_checks += msg->m_checkcount;
 	dbentry->n_checksum_failures += msg->m_failurecount;
+
+	if (msg->m_failurecount > 0)
+	{
+		Assert(msg->m_failure_time != 0);
+		dbentry->last_checksum_failure = msg->m_failure_time;
+	}
 }
 
 /* ----------
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 537f09e342..5bd5fdb78c 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1373,7 +1373,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 	bool		block_retry = false;
 	char		buf[TAR_SEND_SIZE];
 	uint16		checksum;
-	int			checksum_failures = 0;
+	int			checksum_checks = 0, checksum_failures = 0;
 	off_t		cnt;
 	int			i;
 	pgoff_t		len = 0;
@@ -1527,6 +1527,8 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 											"failures in file \"%s\" will not "
 											"be reported", readfilename)));
 					}
+					else if (block_retry == false)
+						checksum_checks++;
 				}
 				block_retry = false;
 				blkno++;
@@ -1583,10 +1585,9 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 		ereport(WARNING,
 				(errmsg("file \"%s\" has a total of %d checksum verification "
 						"failures", readfilename, checksum_failures)));
-
-		if (dboid != InvalidOid)
-			pgstat_report_checksum_failures_in_db(dboid, checksum_failures);
 	}
+
+	pgstat_report_checksum_in_db(dboid, checksum_checks, checksum_failures);
 	total_checksum_failures += checksum_failures;
 
 	return true;
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 14bc61b8ad..9428e7cfa1 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -152,11 +152,13 @@ PageIsVerified(Page page, BlockNumber blkno)
 				 errmsg("page verification failed, calculated checksum %u but expected %u",
 						checksum, p->pd_checksum)));
 
-		pgstat_report_checksum_failure();
+		pgstat_report_checksum(true);
 
 		if (header_sane && ignore_checksum_failure)
 			return true;
 	}
+	else
+		pgstat_report_checksum(false);
 
 	return false;
 }
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index da1d685c08..5faf8c156c 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1497,6 +1497,21 @@ pg_stat_get_db_deadlocks(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_db_checksum_checks(PG_FUNCTION_ARGS)
+{
+	Oid			dbid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatDBEntry *dbentry;
+
+	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (dbentry->n_checksum_checks);
+
+	PG_RETURN_INT64(result);
+}
+
 Datum
 pg_stat_get_db_checksum_failures(PG_FUNCTION_ARGS)
 {
@@ -1512,6 +1527,24 @@ pg_stat_get_db_checksum_failures(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_db_checksum_last_failure(PG_FUNCTION_ARGS)
+{
+	Oid			dbid = PG_GETARG_OID(0);
+	TimestampTz result;
+	PgStat_StatDBEntry *dbentry;
+
+	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
+		result = 0;
+	else
+		result = dbentry->last_checksum_failure;
+
+	if (result == 0)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_TIMESTAMPTZ(result);
+}
+
 Datum
 pg_stat_get_db_blk_read_time(PG_FUNCTION_ARGS)
 {
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index c4b012cf4c..43b93698b7 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5253,10 +5253,20 @@
   prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_db_deadlocks' },
 { oid => '3426',
+  descr => 'statistics: checksum checks done in database',
+  proname => 'pg_stat_get_db_checksum_checks', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_checksum_checks' },
+{ oid => '3427',
   descr => 'statistics: checksum failures detected in database',
   proname => 'pg_stat_get_db_checksum_failures', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_db_checksum_failures' },
+{ oid => '3428',
+  descr => 'statistics: last checksum failure detected in database',
+  proname => 'pg_stat_get_db_checksum_last_failure', provolatile => 's',
+  proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_checksum_last_failure' },
 { oid => '3074', descr => 'statistics: last reset for a database',
   proname => 'pg_stat_get_db_stat_reset_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 725c8b0d64..7f19e2e451 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -65,7 +65,7 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
 	PGSTAT_MTYPE_DEADLOCK,
-	PGSTAT_MTYPE_CHECKSUMFAILURE
+	PGSTAT_MTYPE_CHECKSUM
 } StatMsgType;
 
 /* ----------
@@ -532,16 +532,18 @@ typedef struct PgStat_MsgDeadlock
 } PgStat_MsgDeadlock;
 
 /* ----------
- * PgStat_MsgChecksumFailure	Sent by the backend to tell the collector
- *								about checksum failures noticed.
+ * PgStat_MsgChecksum	Sent by the backend to tell the collector
+ *								about checksum verficiations done.
  * ----------
  */
-typedef struct PgStat_MsgChecksumFailure
+typedef struct PgStat_MsgChecksum
 {
 	PgStat_MsgHdr m_hdr;
 	Oid			m_databaseid;
+	int			m_checkcount;
 	int			m_failurecount;
-} PgStat_MsgChecksumFailure;
+	TimestampTz	m_failure_time;
+} PgStat_MsgChecksum;
 
 
 /* ----------
@@ -606,7 +608,9 @@ typedef struct PgStat_StatDBEntry
 	PgStat_Counter n_temp_files;
 	PgStat_Counter n_temp_bytes;
 	PgStat_Counter n_deadlocks;
+	PgStat_Counter n_checksum_checks;
 	PgStat_Counter n_checksum_failures;
+	TimestampTz last_checksum_failure;
 	PgStat_Counter n_block_read_time;	/* times in microseconds */
 	PgStat_Counter n_block_write_time;
 
@@ -678,14 +682,14 @@ typedef struct PgStat_StatFuncEntry
  */
 typedef struct PgStat_ArchiverStats
 {
-	PgStat_Counter archived_count;	/* archival successes */
-	char		last_archived_wal[MAX_XFN_CHARS + 1];	/* last WAL file
+	PgStat_Counter archived_count;  /* archival successes */
+	char        last_archived_wal[MAX_XFN_CHARS + 1];   /* last WAL file
 														 * archived */
-	TimestampTz last_archived_timestamp;	/* last archival success time */
-	PgStat_Counter failed_count;	/* failed archival attempts */
-	char		last_failed_wal[MAX_XFN_CHARS + 1]; /* WAL file involved in
+	TimestampTz last_archived_timestamp;    /* last archival success time */
+	PgStat_Counter failed_count;    /* failed archival attempts */
+	char        last_failed_wal[MAX_XFN_CHARS + 1]; /* WAL file involved in
 													 * last failure */
-	TimestampTz last_failed_timestamp;	/* last archival failure time */
+	TimestampTz last_failed_timestamp;  /* last archival failure time */
 	TimestampTz stat_reset_timestamp;
 } PgStat_ArchiverStats;
 
@@ -1214,8 +1218,8 @@ extern void pgstat_report_analyze(Relation rel,
 
 extern void pgstat_report_recovery_conflict(int reason);
 extern void pgstat_report_deadlock(void);
-extern void pgstat_report_checksum_failures_in_db(Oid dboid, int failurecount);
-extern void pgstat_report_checksum_failure(void);
+extern void pgstat_report_checksum_in_db(Oid dboid, int checkcount, int failurecount);
+extern void pgstat_report_checksum(bool failed);
 
 extern void pgstat_initialize(void);
 extern void pgstat_bestart(void);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f104dc4a62..c73f1c5edd 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1801,6 +1801,18 @@ pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints
     pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync,
     pg_stat_get_buf_alloc() AS buffers_alloc,
     pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
+pg_stat_checksum| SELECT d.oid AS datid,
+    d.datname,
+    pg_stat_get_db_checksum_checks(d.oid) AS checksum_checks,
+    pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures,
+    pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure,
+    pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset
+   FROM ( SELECT pg_database.oid,
+            pg_database.datname
+           FROM pg_database
+        UNION ALL
+         SELECT 0,
+            '<shared objects>'::name) d;
 pg_stat_database| SELECT d.oid AS datid,
     d.datname,
     pg_stat_get_db_numbackends(d.oid) AS numbackends,
@@ -1817,7 +1829,6 @@ pg_stat_database| SELECT d.oid AS datid,
     pg_stat_get_db_temp_files(d.oid) AS temp_files,
     pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes,
     pg_stat_get_db_deadlocks(d.oid) AS deadlocks,
-    pg_stat_get_db_checksum_failures(d.oid) AS checksum_failures,
     pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time,
     pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time,
     pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset
