diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc
index ebe98791df8b73ead953d477986c94a9a780a512..dd63c55af3f3b50c20fb8955ce8e121ba0b23cc1 100644
--- a/extra/mariabackup/xtrabackup.cc
+++ b/extra/mariabackup/xtrabackup.cc
@@ -2396,10 +2396,15 @@ static bool innodb_init()
   buf_flush_sync();
   recv_sys.debug_free();
   ut_ad(!os_aio_pending_reads());
-  ut_ad(!os_aio_pending_writes());
   ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex));
   ut_ad(!buf_pool.get_oldest_modification(0));
   ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex));
+  /* os_aio_pending_writes() may hold here if some write_io_callback()
+  did not release the slot yet.  However, the page write itself must
+  have completed, because the buf_pool.flush_list is empty. In debug
+  builds, we wait for this to happen, hoping to get a hung process if
+  this assumption does not hold. */
+  ut_d(os_aio_wait_until_no_pending_writes(false));
   log_sys.close_file();
 
   if (xtrabackup_incremental)
diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc
index 27f9a35fb4c1e598d1af46ba7d3f9e84a83f6c22..bcd18e73926aedc005406f9301d4ad901fda430d 100644
--- a/storage/innobase/fil/fil0fil.cc
+++ b/storage/innobase/fil/fil0fil.cc
@@ -2804,6 +2804,7 @@ void IORequest::write_complete(int io_error) const
   ut_ad(fil_validate_skip());
   ut_ad(node);
   ut_ad(is_write());
+  node->complete_write();
 
   if (!bpage)
   {
@@ -2816,7 +2817,6 @@ void IORequest::write_complete(int io_error) const
   else
     buf_page_write_complete(*this, io_error);
 
-  node->complete_write();
   node->space->release();
 }
 
diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc
index 31876605763b26c237e2b7ca91efeb5c90f90141..4783408f2f9ebd8342f4d2a01a611eced11e620c 100644
--- a/storage/innobase/srv/srv0start.cc
+++ b/storage/innobase/srv/srv0start.cc
@@ -183,10 +183,16 @@ static dberr_t create_log_file(bool create_new_db, lsn_t lsn)
 	delete_log_files();
 
 	ut_ad(!os_aio_pending_reads());
-	ut_ad(!os_aio_pending_writes());
 	ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex));
 	ut_ad(!buf_pool.get_oldest_modification(0));
 	ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex));
+	/* os_aio_pending_writes() may hold here if some
+	write_io_callback() did not release the slot yet.  However,
+	the page write itself must have completed, because the
+	buf_pool.flush_list is empty. In debug builds, we wait for
+	this to happen, hoping to get a hung process if this
+	assumption does not hold. */
+	ut_d(os_aio_wait_until_no_pending_writes(false));
 
 	log_sys.latch.wr_lock(SRW_LOCK_CALL);
 	log_sys.set_capacity();
@@ -1388,10 +1394,17 @@ dberr_t srv_start(bool create_new_db)
 			end of create_log_file(). */
 			ut_d(recv_no_log_write = true);
 			ut_ad(!os_aio_pending_reads());
-			ut_ad(!os_aio_pending_writes());
 			ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex));
 			ut_ad(!buf_pool.get_oldest_modification(0));
 			ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex));
+			/* os_aio_pending_writes() may hold here if
+			some write_io_callback() did not release the
+			slot yet. However, the page write itself must
+			have completed, because the buf_pool.flush_list
+			is empty. In debug builds, we wait for this to
+			happen, hoping to get a hung process if this
+			assumption does not hold. */
+			ut_d(os_aio_wait_until_no_pending_writes(false));
 
 			/* Close the redo log file, so that we can replace it */
 			log_sys.close_file();