summaryrefslogtreecommitdiff
path: root/storage/innobase/fil/fil0fil.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/fil/fil0fil.cc')
-rw-r--r--storage/innobase/fil/fil0fil.cc709
1 files changed, 269 insertions, 440 deletions
diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc
index 844291db939..b627e7826a5 100644
--- a/storage/innobase/fil/fil0fil.cc
+++ b/storage/innobase/fil/fil0fil.cc
@@ -173,12 +173,6 @@ fil_system_t fil_system;
UNIV_INTERN extern uint srv_fil_crypt_rotate_key_age;
UNIV_INTERN extern ib_mutex_t fil_crypt_threads_mutex;
-/** Determine if user has explicitly disabled fsync(). */
-# define fil_buffering_disabled(s) \
- ((s)->purpose == FIL_TYPE_TABLESPACE \
- && srv_file_flush_method \
- == SRV_O_DIRECT_NO_FSYNC)
-
/** Determine if the space id is a user tablespace id or not.
@param[in] space_id Space ID to check
@return true if it is a user tablespace ID */
@@ -249,67 +243,6 @@ fil_node_prepare_for_io(
fil_node_t* node, /*!< in: file node */
fil_space_t* space); /*!< in: space */
-/** Update the data structures when an i/o operation finishes.
-@param[in,out] node file node
-@param[in] type IO context */
-static
-void
-fil_node_complete_io(fil_node_t* node, const IORequest& type);
-
-/** Reads data from a space to a buffer. Remember that the possible incomplete
-blocks at the end of file are ignored: they are not taken into account when
-calculating the byte offset within a space.
-@param[in] page_id page id
-@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
-@param[in] byte_offset remainder of offset in bytes; in aio this
-must be divisible by the OS block size
-@param[in] len how many bytes to read; this must not cross a
-file boundary; in aio this must be a block size multiple
-@param[in,out] buf buffer where to store data read; in aio this
-must be appropriately aligned
-@return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do
-i/o on a tablespace which does not exist */
-UNIV_INLINE
-dberr_t
-fil_read(
- const page_id_t page_id,
- ulint zip_size,
- ulint byte_offset,
- ulint len,
- void* buf)
-{
- return(fil_io(IORequestRead, true, page_id, zip_size,
- byte_offset, len, buf, NULL));
-}
-
-/** Writes data to a space from a buffer. Remember that the possible incomplete
-blocks at the end of file are ignored: they are not taken into account when
-calculating the byte offset within a space.
-@param[in] page_id page id
-@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
-@param[in] byte_offset remainder of offset in bytes; in aio this
-must be divisible by the OS block size
-@param[in] len how many bytes to write; this must not cross
-a file boundary; in aio this must be a block size multiple
-@param[in] buf buffer from which to write; in aio this must
-be appropriately aligned
-@return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do
-i/o on a tablespace which does not exist */
-UNIV_INLINE
-dberr_t
-fil_write(
- const page_id_t page_id,
- ulint zip_size,
- ulint byte_offset,
- ulint len,
- void* buf)
-{
- ut_ad(!srv_read_only_mode);
-
- return(fil_io(IORequestWrite, true, page_id, zip_size,
- byte_offset, len, buf, NULL));
-}
-
/*******************************************************************//**
Returns the table space by a given id, NULL if not found.
It is unsafe to dereference the returned pointer. It is fine to check
@@ -395,8 +328,7 @@ fil_space_is_flushed(
node = UT_LIST_GET_NEXT(chain, node)) {
if (node->needs_flush) {
-
- ut_ad(!fil_buffering_disabled(space));
+ ut_ad(srv_file_flush_method != SRV_O_DIRECT_NO_FSYNC);
return(false);
}
}
@@ -678,8 +610,7 @@ static void fil_flush_low(fil_space_t* space, bool metadata = false)
ut_ad(space);
ut_ad(!space->stop_new_ops);
- if (fil_buffering_disabled(space)) {
-
+ if (srv_file_flush_method == SRV_O_DIRECT_NO_FSYNC) {
/* No need to flush. User has explicitly disabled
buffering. */
ut_ad(!space->is_in_unflushed_spaces);
@@ -839,7 +770,7 @@ fil_space_extend_must_retry(
const ulint pages_in_MiB = node->size
& ~ulint((1U << (20U - srv_page_size_shift)) - 1);
- fil_node_complete_io(node,IORequestRead);
+ node->complete_io();
/* Keep the last data file size info up to date, rounded to
full megabytes */
@@ -864,14 +795,12 @@ fil_space_extend_must_retry(
}
}
-/*******************************************************************//**
-Reserves the fil_system.mutex and tries to make sure we can open at least one
+/** Acquire fil_system.mutex and try to make sure we can open at least one
file while holding it. This should be called before calling
fil_node_prepare_for_io(), because that function may need to open a file. */
static
-void
+fil_space_t*
fil_mutex_enter_and_prepare_for_io(
-/*===============================*/
ulint space_id) /*!< in: space id */
{
for (ulint count = 0;;) {
@@ -879,8 +808,8 @@ fil_mutex_enter_and_prepare_for_io(
fil_space_t* space = fil_space_get_by_id(space_id);
- if (space == NULL) {
- break;
+ if (!space) {
+ return nullptr;
}
fil_node_t* node = UT_LIST_GET_LAST(space->chain);
@@ -960,7 +889,7 @@ fil_mutex_enter_and_prepare_for_io(
}
}
- break;
+ return space;
}
}
@@ -987,87 +916,96 @@ fil_space_extend(
return(success);
}
-/** Prepare to free a file node object from a tablespace memory cache.
-@param[in,out] node file node
-@param[in] space tablespace */
-static
-void
-fil_node_close_to_free(
- fil_node_t* node,
- fil_space_t* space)
+/** Prepare to free a file from fil_system. */
+inline void fil_node_t::close_to_free()
{
- ut_ad(mutex_own(&fil_system.mutex));
- ut_a(node->magic_n == FIL_NODE_MAGIC_N);
- ut_a(node->n_pending == 0);
- ut_a(!node->being_extended);
-
- if (node->is_open()) {
- /* We fool the assertion in fil_node_t::close() to think
- there are no unflushed modifications in the file */
-
- node->needs_flush = false;
-
- if (fil_buffering_disabled(space)) {
-
- ut_ad(!space->is_in_unflushed_spaces);
- ut_ad(fil_space_is_flushed(space));
-
- } else if (space->is_in_unflushed_spaces
- && fil_space_is_flushed(space)) {
+ ut_ad(mutex_own(&fil_system.mutex));
+ ut_a(magic_n == FIL_NODE_MAGIC_N);
+ ut_a(!being_extended);
- fil_system.unflushed_spaces.remove(*space);
- space->is_in_unflushed_spaces = false;
- }
-
- node->close();
- }
+ while (is_open())
+ {
+ if (space->is_in_unflushed_spaces)
+ {
+ ut_ad(srv_file_flush_method != SRV_O_DIRECT_NO_FSYNC);
+ space->is_in_unflushed_spaces= false;
+ fil_system.unflushed_spaces.remove(*space);
+ }
+
+ if (n_pending)
+ {
+ mutex_exit(&fil_system.mutex);
+ os_thread_sleep(100);
+ mutex_enter(&fil_system.mutex);
+ continue;
+ }
+
+ if (srv_file_flush_method == SRV_O_DIRECT_NO_FSYNC)
+ {
+ ut_ad(!space->is_in_unflushed_spaces);
+ ut_ad(fil_space_is_flushed(space));
+ }
+ else if (space->is_in_unflushed_spaces && fil_space_is_flushed(space))
+ {
+ space->is_in_unflushed_spaces= false;
+ fil_system.unflushed_spaces.remove(*space);
+ }
+
+ if (fil_space_belongs_in_lru(space))
+ {
+ ut_ad(UT_LIST_GET_LEN(fil_system.LRU) > 0);
+ UT_LIST_REMOVE(fil_system.LRU, this);
+ }
+ ut_a(!n_pending_flushes);
+ ut_a(!being_extended);
+ bool ret= os_file_close(handle);
+ ut_a(ret);
+ handle= OS_FILE_CLOSED;
+ break;
+ }
}
-/** Detach a space object from the tablespace memory cache.
-Closes the files in the chain but does not delete them.
-There must not be any pending i/o's or flushes on the files.
-@param[in,out] space tablespace */
-static
-void
-fil_space_detach(
- fil_space_t* space)
+/** Detach a tablespace from the cache and close the files. */
+inline void fil_system_t::detach(fil_space_t *space)
{
- ut_ad(mutex_own(&fil_system.mutex));
-
- HASH_DELETE(fil_space_t, hash, fil_system.spaces, space->id, space);
-
- if (space->is_in_unflushed_spaces) {
+ ut_ad(mutex_own(&fil_system.mutex));
+ HASH_DELETE(fil_space_t, hash, spaces, space->id, space);
- ut_ad(!fil_buffering_disabled(space));
- fil_system.unflushed_spaces.remove(*space);
- space->is_in_unflushed_spaces = false;
- }
-
- if (space->is_in_rotation_list) {
- fil_system.rotation_list.remove(*space);
- space->is_in_rotation_list = false;
- }
-
- UT_LIST_REMOVE(fil_system.space_list, space);
-
- ut_a(space->magic_n == FIL_SPACE_MAGIC_N);
- ut_a(space->n_pending_flushes == 0);
-
- for (fil_node_t* fil_node = UT_LIST_GET_FIRST(space->chain);
- fil_node != NULL;
- fil_node = UT_LIST_GET_NEXT(chain, fil_node)) {
-
- fil_node_close_to_free(fil_node, space);
- }
+ if (space->is_in_unflushed_spaces)
+ {
+ ut_ad(srv_file_flush_method != SRV_O_DIRECT_NO_FSYNC);
+ space->is_in_unflushed_spaces= false;
+ unflushed_spaces.remove(*space);
+ }
- if (space == fil_system.sys_space) {
- fil_system.sys_space = NULL;
- } else if (space == fil_system.temp_space) {
- fil_system.temp_space = NULL;
- }
+ if (space->is_in_rotation_list)
+ {
+ space->is_in_rotation_list= false;
+ rotation_list.remove(*space);
+ }
+ UT_LIST_REMOVE(space_list, space);
+ if (space == sys_space)
+ sys_space= nullptr;
+ else if (space == temp_space)
+ temp_space= nullptr;
+
+ ut_a(space->magic_n == FIL_SPACE_MAGIC_N);
+ ut_a(space->n_pending_flushes == 0);
+
+ for (fil_node_t* node= UT_LIST_GET_FIRST(space->chain); node;
+ node= UT_LIST_GET_NEXT(chain, node))
+ if (node->is_open())
+ {
+ ut_ad(n_open > 0);
+ n_open--;
+ }
+
+ for (fil_node_t* node= UT_LIST_GET_FIRST(space->chain); node;
+ node= UT_LIST_GET_NEXT(chain, node))
+ node->close_to_free();
}
-/** Free a tablespace object on which fil_space_detach() was invoked.
+/** Free a tablespace object on which fil_system_t::detach() was invoked.
There must not be any pending i/o's or flushes on the files.
@param[in,out] space tablespace */
static
@@ -1080,7 +1018,7 @@ fil_space_free_low(
|| space->max_lsn == 0);
/* Wait for fil_space_t::release_for_io(); after
- fil_space_detach(), the tablespace cannot be found, so
+ fil_system_t::detach(), the tablespace cannot be found, so
fil_space_acquire_for_io() would return NULL */
while (space->pending_io()) {
os_thread_sleep(100);
@@ -1121,7 +1059,7 @@ fil_space_free(
fil_space_t* space = fil_space_get_by_id(id);
if (space != NULL) {
- fil_space_detach(space);
+ fil_system.detach(space);
}
mutex_exit(&fil_system.mutex);
@@ -1332,9 +1270,7 @@ fil_space_t* fil_system_t::read_page0(ulint id)
/* It is possible that the tablespace is dropped while we are
not holding the mutex. */
- fil_mutex_enter_and_prepare_for_io(id);
-
- fil_space_t* space = fil_space_get_by_id(id);
+ fil_space_t* space = fil_mutex_enter_and_prepare_for_io(id);
if (space == NULL || UT_LIST_GET_LEN(space->chain) == 0) {
return(NULL);
@@ -1356,7 +1292,7 @@ fil_space_t* fil_system_t::read_page0(ulint id)
return(NULL);
}
- fil_node_complete_io(node, IORequestRead);
+ node->complete_io();
return space;
}
@@ -1634,25 +1570,24 @@ fil_open_system_tablespace_files()
mutex_exit(&fil_system.mutex);
}
-/*******************************************************************//**
-Closes all open files. There must not be any pending i/o's or not flushed
-modifications in the files. */
-void
-fil_close_all_files(void)
-/*=====================*/
+/** Close all tablespace files at shutdown */
+void fil_close_all_files()
{
+ if (!fil_system.is_initialised()) {
+ return;
+ }
+
fil_space_t* space;
/* At shutdown, we should not have any files in this list. */
- ut_ad(fil_system.is_initialised());
ut_ad(srv_fast_shutdown == 2
|| !srv_was_started
|| UT_LIST_GET_LEN(fil_system.named_spaces) == 0);
+ fil_flush_file_spaces();
mutex_enter(&fil_system.mutex);
- for (space = UT_LIST_GET_FIRST(fil_system.space_list);
- space != NULL; ) {
+ for (space = UT_LIST_GET_FIRST(fil_system.space_list); space; ) {
fil_node_t* node;
fil_space_t* prev_space = space;
@@ -1660,13 +1595,31 @@ fil_close_all_files(void)
node != NULL;
node = UT_LIST_GET_NEXT(chain, node)) {
- if (node->is_open()) {
- node->close();
+ if (!node->is_open()) {
+next:
+ continue;
}
+
+ for (ulint count = 10000; count--; ) {
+ mutex_exit(&fil_system.mutex);
+ os_thread_sleep(100);
+ mutex_enter(&fil_system.mutex);
+ if (!node->is_open()) {
+ goto next;
+ }
+ if (!node->n_pending) {
+ node->close();
+ goto next;
+ }
+ }
+
+ ib::error() << "File '" << node->name
+ << "' has " << node->n_pending
+ << " operations";
}
space = UT_LIST_GET_NEXT(space_list, space);
- fil_space_detach(prev_space);
+ fil_system.detach(prev_space);
fil_space_free_low(prev_space);
}
@@ -1708,15 +1661,17 @@ fil_write_flushed_lsn(
lsn_t lsn)
{
byte* buf;
- dberr_t err = DB_TABLESPACE_NOT_FOUND;
+ ut_ad(!srv_read_only_mode);
buf = static_cast<byte*>(aligned_malloc(srv_page_size, srv_page_size));
const page_id_t page_id(TRX_SYS_SPACE, 0);
- err = fil_read(page_id, 0, 0, srv_page_size, buf);
+ fil_io_t fio = fil_io(IORequestRead, true, page_id, 0, 0,
+ srv_page_size, buf, NULL);
- if (err == DB_SUCCESS) {
+ if (fio.err == DB_SUCCESS) {
+ fio.node->space->release_for_io();
mach_write_to_8(buf + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, lsn);
ulint fsp_flags = mach_read_from_4(
@@ -1726,12 +1681,17 @@ fil_write_flushed_lsn(
buf_flush_assign_full_crc32_checksum(buf);
}
- err = fil_write(page_id, 0, 0, srv_page_size, buf);
+ fio = fil_io(IORequestWrite, true, page_id, 0, 0,
+ srv_page_size, buf, NULL);
fil_flush_file_spaces();
}
+ if (fio.node) {
+ fio.node->space->release_for_io();
+ }
+
aligned_free(buf);
- return(err);
+ return fio.err;
}
/** Acquire a tablespace when it could be dropped concurrently.
@@ -2008,13 +1968,6 @@ fil_op_replay_rename(
return(true);
}
-/** File operations for tablespace */
-enum fil_operation_t {
- FIL_OPERATION_DELETE, /*!< delete a single-table tablespace */
- FIL_OPERATION_CLOSE, /*!< close a single-table tablespace */
- FIL_OPERATION_TRUNCATE /*!< truncate an undo tablespace */
-};
-
/** Check for pending operations.
@param[in] space tablespace
@param[in] count number of attempts so far
@@ -2050,7 +2003,6 @@ static
ulint
fil_check_pending_io(
/*=================*/
- fil_operation_t operation, /*!< in: File operation */
fil_space_t* space, /*!< in/out: Tablespace to check */
fil_node_t** node, /*!< out: Node in space list */
ulint count) /*!< in: number of attempts so far */
@@ -2058,15 +2010,6 @@ fil_check_pending_io(
ut_ad(mutex_own(&fil_system.mutex));
ut_ad(!space->referenced());
- switch (operation) {
- case FIL_OPERATION_DELETE:
- case FIL_OPERATION_CLOSE:
- break;
- case FIL_OPERATION_TRUNCATE:
- space->is_being_truncated = true;
- break;
- }
-
/* The following code must change when InnoDB supports
multiple datafiles per tablespace. */
ut_a(UT_LIST_GET_LEN(space->chain) == 1);
@@ -2095,24 +2038,18 @@ fil_check_pending_io(
/*******************************************************************//**
Check pending operations on a tablespace.
-@return DB_SUCCESS or error failure. */
+@return tablespace */
static
-dberr_t
+fil_space_t*
fil_check_pending_operations(
/*=========================*/
ulint id, /*!< in: space id */
- fil_operation_t operation, /*!< in: File operation */
- fil_space_t** space, /*!< out: tablespace instance
- in memory */
+ bool truncate, /*!< in: whether to truncate a file */
char** path) /*!< out/own: tablespace path */
{
ulint count = 0;
ut_a(!is_system_tablespace(id));
- ut_ad(space);
-
- *space = 0;
-
mutex_enter(&fil_system.mutex);
fil_space_t* sp = fil_space_get_by_id(id);
@@ -2130,32 +2067,31 @@ fil_check_pending_operations(
/* Check for pending operations. */
do {
- sp = fil_space_get_by_id(id);
-
count = fil_check_pending_ops(sp, count);
mutex_exit(&fil_system.mutex);
- if (count > 0) {
+ if (count) {
os_thread_sleep(20000); // Wait 0.02 seconds
+ } else if (!sp) {
+ return nullptr;
}
mutex_enter(&fil_system.mutex);
- } while (count > 0);
+
+ sp = fil_space_get_by_id(id);
+ } while (count);
/* Check for pending IO. */
for (;;) {
- sp = fil_space_get_by_id(id);
-
- if (sp == NULL) {
- mutex_exit(&fil_system.mutex);
- return(DB_TABLESPACE_NOT_FOUND);
+ if (truncate) {
+ sp->is_being_truncated = true;
}
fil_node_t* node;
- count = fil_check_pending_io(operation, sp, &node, count);
+ count = fil_check_pending_io(sp, &node, count);
if (count == 0 && path) {
*path = mem_strdup(node->name);
@@ -2169,40 +2105,29 @@ fil_check_pending_operations(
os_thread_sleep(20000); // Wait 0.02 seconds
mutex_enter(&fil_system.mutex);
- }
+ sp = fil_space_get_by_id(id);
- ut_ad(sp);
+ if (!sp) {
+ mutex_exit(&fil_system.mutex);
+ break;
+ }
+ }
- *space = sp;
- return(DB_SUCCESS);
+ return sp;
}
-/*******************************************************************//**
-Closes a single-table tablespace. The tablespace must be cached in the
-memory cache. Free all pages used by the tablespace.
-@return DB_SUCCESS or error */
-dberr_t
-fil_close_tablespace(
-/*=================*/
- trx_t* trx, /*!< in/out: Transaction covering the close */
- ulint id) /*!< in: space id */
+/** Close a single-table tablespace on failed IMPORT TABLESPACE.
+The tablespace must be cached in the memory cache.
+Free all pages used by the tablespace. */
+void fil_close_tablespace(ulint id)
{
- char* path = 0;
- fil_space_t* space = 0;
- dberr_t err;
-
- ut_a(!is_system_tablespace(id));
-
- err = fil_check_pending_operations(id, FIL_OPERATION_CLOSE,
- &space, &path);
-
- if (err != DB_SUCCESS) {
- return(err);
+ ut_ad(!is_system_tablespace(id));
+ char* path = nullptr;
+ fil_space_t* space = fil_check_pending_operations(id, false, &path);
+ if (!space) {
+ return;
}
- ut_a(space);
- ut_a(path != 0);
-
rw_lock_x_lock(&space->latch);
/* Invalidate in the buffer pool all pages belonging to the
@@ -2218,23 +2143,17 @@ fil_close_tablespace(
if (!fil_space_free(id, true)) {
rw_lock_x_unlock(&space->latch);
- err = DB_TABLESPACE_NOT_FOUND;
- } else {
- err = DB_SUCCESS;
}
/* If it is a delete then also delete any generated files, otherwise
when we drop the database the remove directory will fail. */
- char* cfg_name = fil_make_filepath(path, NULL, CFG, false);
- if (cfg_name != NULL) {
+ if (char* cfg_name = fil_make_filepath(path, NULL, CFG, false)) {
os_file_delete_if_exists(innodb_data_file_key, cfg_name, NULL);
ut_free(cfg_name);
}
ut_free(path);
-
- return(err);
}
/** Determine whether a table can be accessed in operations that are
@@ -2264,15 +2183,14 @@ bool fil_table_accessible(const dict_table_t* table)
@return DB_SUCCESS or error */
dberr_t fil_delete_tablespace(ulint id, bool if_exists)
{
- char* path = 0;
- fil_space_t* space = 0;
-
- ut_a(!is_system_tablespace(id));
+ char* path = NULL;
+ ut_ad(!is_system_tablespace(id));
- dberr_t err = fil_check_pending_operations(
- id, FIL_OPERATION_DELETE, &space, &path);
+ dberr_t err;
+ fil_space_t *space = fil_check_pending_operations(id, false, &path);
- if (err != DB_SUCCESS) {
+ if (!space) {
+ err = DB_TABLESPACE_NOT_FOUND;
if (!if_exists) {
ib::error() << "Cannot delete tablespace " << id
<< " because it is not found"
@@ -2282,9 +2200,6 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists)
goto func_exit;
}
- ut_a(space);
- ut_a(path != 0);
-
/* IMPORTANT: Because we have set space::stop_new_ops there
can't be any new reads or flushes. We are here
because node::n_pending was zero above. However, it is still
@@ -2306,6 +2221,7 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists)
To deal with potential read requests, we will check the
::stop_new_ops flag in fil_io(). */
+ err = DB_SUCCESS;
buf_LRU_flush_or_remove_pages(id, false);
/* If it is a delete then also delete any generated files, otherwise
@@ -2344,10 +2260,7 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists)
ut_a(s == space);
ut_a(!space->referenced());
ut_a(UT_LIST_GET_LEN(space->chain) == 1);
- fil_node_t* node = UT_LIST_GET_FIRST(space->chain);
- ut_a(node->n_pending == 0);
-
- fil_space_detach(space);
+ fil_system.detach(space);
mutex_exit(&fil_system.mutex);
log_mutex_enter();
@@ -2384,17 +2297,9 @@ func_exit:
@param[in] space_id undo tablespace id
@return the tablespace
@retval NULL if tablespace not found */
-fil_space_t* fil_truncate_prepare(ulint space_id)
+fil_space_t *fil_truncate_prepare(ulint space_id)
{
- /* Stop all I/O on the tablespace and ensure that related
- pages are flushed to disk. */
- fil_space_t* space;
- if (fil_check_pending_operations(space_id, FIL_OPERATION_TRUNCATE,
- &space, NULL) != DB_SUCCESS) {
- return NULL;
- }
- ut_ad(space != NULL);
- return space;
+ return fil_check_pending_operations(space_id, true, nullptr);
}
/*******************************************************************//**
@@ -3791,83 +3696,24 @@ fil_node_prepare_for_io(
}
}
- if (node->n_pending == 0 && fil_space_belongs_in_lru(space)) {
- /* The node is in the LRU list, remove it */
- ut_a(UT_LIST_GET_LEN(fil_system.LRU) > 0);
+ if (node->n_pending++ == 0 && fil_space_belongs_in_lru(space)) {
UT_LIST_REMOVE(fil_system.LRU, node);
}
- node->n_pending++;
-
return(true);
}
-/** Update the data structures when an i/o operation finishes.
-@param[in,out] node file node
-@param[in] type IO context */
-static
-void
-fil_node_complete_io(fil_node_t* node, const IORequest& type)
-{
- ut_ad(mutex_own(&fil_system.mutex));
- ut_a(node->n_pending > 0);
-
- --node->n_pending;
-
- ut_ad(type.validate());
-
- if (type.is_write()) {
-
- ut_ad(!srv_read_only_mode
- || node->space->purpose == FIL_TYPE_TEMPORARY);
-
- if (fil_buffering_disabled(node->space)) {
-
- /* We don't need to keep track of unflushed
- changes as user has explicitly disabled
- buffering. */
- ut_ad(!node->space->is_in_unflushed_spaces);
- ut_ad(node->needs_flush == false);
-
- } else {
- node->needs_flush = true;
-
- if (!node->space->is_in_unflushed_spaces) {
- node->space->is_in_unflushed_spaces = true;
- fil_system.unflushed_spaces.push_front(
- *node->space);
- }
- }
- }
-
- if (node->n_pending == 0 && fil_space_belongs_in_lru(node->space)) {
-
- /* The node must be put back to the LRU list */
- UT_LIST_ADD_FIRST(fil_system.LRU, node);
- }
-}
-
/** Report information about an invalid page access. */
-static
-void
-fil_report_invalid_page_access(
- ulint block_offset, /*!< in: block offset */
- ulint space_id, /*!< in: space id */
- const char* space_name, /*!< in: space name */
- ulint byte_offset, /*!< in: byte offset */
- ulint len, /*!< in: I/O length */
- bool is_read) /*!< in: I/O type */
+ATTRIBUTE_COLD __attribute__((noreturn))
+static void
+fil_report_invalid_page_access(const page_id_t id, const char *name,
+ ulint byte_offset, ulint len, bool is_read)
{
ib::fatal()
- << "Trying to " << (is_read ? "read" : "write")
- << " page number " << block_offset << " in"
- " space " << space_id << ", space name " << space_name << ","
- " which is outside the tablespace bounds. Byte offset "
- << byte_offset << ", len " << len <<
- (space_id == 0 && !srv_was_started
- ? "Please check that the configuration matches"
- " the InnoDB system tablespace location (ibdata files)"
- : "");
+ << "Trying to " << (is_read ? "read " : "write ")
+ << id
+ << " which is outside the bounds of tablespace " << name
+ << ". Byte offset " << byte_offset << ", len " << len;
}
inline void IORequest::set_fil_node(fil_node_t* node)
@@ -3895,12 +3741,11 @@ inline void IORequest::set_fil_node(fil_node_t* node)
aligned
@param[in] message message for aio handler if non-sync aio
used, else ignored
-@param[in] ignore whether to ignore out-of-bounds page_id
+@param[in] ignore whether to ignore errors
@param[in] punch_hole punch the hole to the file for page_compressed
tablespace
-@return DB_SUCCESS, or DB_TABLESPACE_DELETED
- if we are trying to do i/o on a tablespace which does not exist */
-dberr_t
+@return status and file descriptor */
+fil_io_t
fil_io(
const IORequest& type,
bool sync,
@@ -3958,23 +3803,18 @@ fil_io(
srv_stats.data_written.add(len);
}
- /* Reserve the fil_system mutex and make sure that we can open at
+ /* Acquire fil_system.mutex and make sure that we can open at
least one file while holding it, if the file is not already open */
+ fil_space_t* space = fil_mutex_enter_and_prepare_for_io(
+ page_id.space());
- fil_mutex_enter_and_prepare_for_io(page_id.space());
-
- fil_space_t* space = fil_space_get_by_id(page_id.space());
-
- /* If we are deleting a tablespace we don't allow async read operations
- on that. However, we do allow write operations and sync read operations. */
- if (space == NULL
+ if (!space
|| (req_type.is_read()
&& !sync
&& space->stop_new_ops
&& !space->is_being_truncated)) {
mutex_exit(&fil_system.mutex);
-
if (!ignore) {
ib::error()
<< "Trying to do I/O to a tablespace which"
@@ -3984,7 +3824,7 @@ fil_io(
<< ", I/O length: " << len << " bytes";
}
- return(DB_TABLESPACE_DELETED);
+ return {DB_TABLESPACE_DELETED, nullptr};
}
ulint cur_page_no = page_id.page_no();
@@ -3995,12 +3835,11 @@ fil_io(
if (node == NULL) {
if (ignore) {
mutex_exit(&fil_system.mutex);
- return(DB_ERROR);
+ return {DB_ERROR, nullptr};
}
fil_report_invalid_page_access(
- page_id.page_no(), page_id.space(),
- space->name, byte_offset, len,
+ page_id, space->name, byte_offset, len,
req_type.is_read());
} else if (fil_is_user_tablespace_id(space->id)
@@ -4022,48 +3861,42 @@ fil_io(
}
/* Open file if closed */
- if (!fil_node_prepare_for_io(node, space)) {
- if (fil_is_user_tablespace_id(space->id)) {
- mutex_exit(&fil_system.mutex);
-
- if (!ignore) {
- ib::error()
- << "Trying to do I/O to a tablespace"
- " which exists without .ibd data file."
- " I/O type: "
- << (req_type.is_read()
- ? "read" : "write")
- << ", page: "
- << page_id_t(page_id.space(),
- cur_page_no)
- << ", I/O length: " << len << " bytes";
- }
+ if (UNIV_UNLIKELY(!fil_node_prepare_for_io(node, space))) {
+ ut_ad(fil_is_user_tablespace_id(space->id));
+ mutex_exit(&fil_system.mutex);
- return(DB_TABLESPACE_DELETED);
+ if (!ignore) {
+ ib::error()
+ << "Trying to do I/O to a tablespace '"
+ << space->name
+ << "' which exists without .ibd data file."
+ " I/O type: "
+ << (req_type.is_read()
+ ? "read" : "write")
+ << ", page: "
+ << page_id
+ << ", I/O length: " << len << " bytes";
}
- /* The tablespace is for log. Currently, we just assert here
- to prevent handling errors along the way fil_io returns.
- Also, if the log files are missing, it would be hard to
- promise the server can continue running. */
- ut_a(0);
+ return {DB_TABLESPACE_DELETED, nullptr};
}
- if (space->id && node->size <= cur_page_no) {
+ if (node->size <= cur_page_no) {
if (ignore) {
/* If we can tolerate the non-existent pages, we
should return with DB_ERROR and let caller decide
what to do. */
- fil_node_complete_io(node, req_type);
+ node->complete_io(req_type.is_write());
mutex_exit(&fil_system.mutex);
- return(DB_ERROR);
+ return {DB_ERROR, nullptr};
}
fil_report_invalid_page_access(
- page_id.page_no(), page_id.space(),
- space->name, byte_offset, len, req_type.is_read());
+ page_id, space->name, byte_offset, len,
+ req_type.is_read());
}
+ space->acquire_for_io();
/* Now we have made the changes in the data structures of fil_system */
mutex_exit(&fil_system.mutex);
@@ -4104,88 +3937,84 @@ fil_io(
the decompression fails or the page is corrupt. */
ut_a(req_type.is_dblwr_recover() || err == DB_SUCCESS);
-
if (sync) {
- /* The i/o operation is already completed when we return from
- os_aio: */
-
mutex_enter(&fil_system.mutex);
-
- fil_node_complete_io(node, req_type);
-
+ node->complete_io(req_type.is_write());
mutex_exit(&fil_system.mutex);
-
ut_ad(fil_validate_skip());
}
-
- return(err);
+ return {err, node};
}
#include <tpool.h>
-/**********************************************************************/
/** Callback for AIO completion */
void fil_aio_callback(os_aio_userdata_t *data)
{
- fil_node_t* node= data->node;
- void* message = data->message;
-
- ut_ad(fil_validate_skip());
+ ut_ad(fil_validate_skip());
+ fil_node_t *node= data->node;
- if (node == NULL) {
- ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
- return;
- }
-
- mutex_enter(&fil_system.mutex);
-
- fil_node_complete_io(node, data->type);
- const ulint space_id= node->space->id;
- bool dblwr = node->space->use_doublewrite();
-
- mutex_exit(&fil_system.mutex);
+ if (UNIV_UNLIKELY(!node))
+ {
+ ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
+ return;
+ }
- ut_ad(fil_validate_skip());
+ ut_ad(data->type.validate());
- /* Do the i/o handling */
- /* IMPORTANT: since i/o handling for reads will read also the insert
- buffer in tablespace 0, you have to be very careful not to introduce
- deadlocks in the i/o system. We keep tablespace 0 data files always
- open, and use a special i/o thread to serve insert buffer requests. */
+ buf_page_t *bpage= static_cast<buf_page_t*>(data->message);
+ if (!bpage)
+ {
+ /* Asynchronous single page writes from the doublewrite buffer
+ don't have access to the page. */
+ ut_ad(data->type.is_write());
+ ut_ad(node->space == fil_system.sys_space);
+ ut_ad(!srv_read_only_mode);
+write_completed:
+ mutex_enter(&fil_system.mutex);
+ node->complete_io(true);
+ mutex_exit(&fil_system.mutex);
+ node->space->release_for_io();
+ return;
+ }
+ if (data->type.is_write())
+ {
+ ut_ad(!srv_read_only_mode || node->space->purpose == FIL_TYPE_TEMPORARY);
+ bool dblwr= node->space->use_doublewrite();
+ if (dblwr && bpage->status == buf_page_t::INIT_ON_FLUSH)
+ {
+ bpage->status= buf_page_t::NORMAL;
+ dblwr= false;
+ }
+ buf_page_write_complete(bpage, data->type, dblwr, false);
+ goto write_completed;
+ }
- /* async single page writes from the dblwr buffer don't have
- access to the page */
- buf_page_t* bpage = static_cast<buf_page_t*>(message);
- if (!bpage) {
- return;
- }
+ ut_ad(data->type.is_read());
- ulint offset = bpage->id.page_no();
- if (dblwr && bpage->status == buf_page_t::INIT_ON_FLUSH) {
- bpage->status = buf_page_t::NORMAL;
- dblwr = false;
- }
- dberr_t err = buf_page_io_complete(bpage, dblwr);
- if (err == DB_SUCCESS) {
- return;
- }
+ /* IMPORTANT: since i/o handling for reads will read also the insert
+ buffer in fil_system.sys_space, we have to be very careful not to
+ introduce deadlocks. We never close the system tablespace (0) data
+ files via fil_system.LRU and we use a dedicated I/O thread to serve
+ change buffer requests. */
+ const page_id_t id(bpage->id());
- ut_ad(data->type.is_read());
- if (recv_recovery_is_on() && !srv_force_recovery) {
- recv_sys.found_corrupt_fs = true;
- }
+ if (dberr_t err= buf_page_read_complete(bpage, *node))
+ {
+ if (recv_recovery_is_on() && !srv_force_recovery)
+ recv_sys.found_corrupt_fs= true;
- if (fil_space_t* space = fil_space_acquire_for_io(space_id)) {
- if (space == node->space) {
- ib::error() << "Failed to read file '" << node->name
- << "' at offset " << offset << ": "
- << ut_strerr(err);
- }
+ ib::error() << "Failed to read page " << id.page_no()
+ << " from file '" << node->name << "': "
+ << ut_strerr(err);
+ }
- space->release_for_io();
- }
+ mutex_enter(&fil_system.mutex);
+ node->complete_io();
+ mutex_exit(&fil_system.mutex);
+ node->space->release_for_io();
}
/**********************************************************************//**