summaryrefslogtreecommitdiff
path: root/storage/innobase/buf/buf0lru.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/buf/buf0lru.cc')
-rw-r--r--storage/innobase/buf/buf0lru.cc576
1 files changed, 138 insertions, 438 deletions
diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc
index 521be10ba21..f9ed938b20c 100644
--- a/storage/innobase/buf/buf0lru.cc
+++ b/storage/innobase/buf/buf0lru.cc
@@ -39,6 +39,9 @@ Created 11/5/1995 Heikki Tuuri
#include "srv0srv.h"
#include "srv0mon.h"
+/** Flush this many pages in buf_LRU_get_free_block() */
+size_t innodb_lru_flush_size;
+
/** The number of blocks from the LRU_old pointer onward, including
the block pointed to, must be buf_pool.LRU_old_ratio/BUF_LRU_OLD_RATIO_DIV
of the whole LRU list length, except that the tolerance defined below
@@ -46,28 +49,13 @@ is allowed. Note that the tolerance must be small enough such that for
even the BUF_LRU_OLD_MIN_LEN long LRU list, the LRU_old pointer is not
allowed to point to either end of the LRU list. */
-static const ulint BUF_LRU_OLD_TOLERANCE = 20;
+static constexpr ulint BUF_LRU_OLD_TOLERANCE = 20;
/** The minimum amount of non-old blocks when the LRU_old list exists
(that is, when there are more than BUF_LRU_OLD_MIN_LEN blocks).
@see buf_LRU_old_adjust_len */
#define BUF_LRU_NON_OLD_MIN_LEN 5
-#ifdef BTR_CUR_HASH_ADAPT
-/** When dropping the search hash index entries before deleting an ibd
-file, we build a local array of pages belonging to that tablespace
-in the buffer pool. Following is the size of that array.
-We also release buf_pool.mutex after scanning this many pages of the
-flush_list when dropping a table. This is to ensure that other threads
-are not blocked for extended period of time when using very large
-buffer pools. */
-static const ulint BUF_LRU_DROP_SEARCH_SIZE = 1024;
-#endif /* BTR_CUR_HASH_ADAPT */
-
-/** We scan these many blocks when looking for a clean page to evict
-during LRU eviction. */
-static const ulint BUF_LRU_SEARCH_SCAN_THRESHOLD = 100;
-
/** If we switch on the InnoDB monitor because there are too few available
frames in the buffer pool, we set this to TRUE */
static bool buf_lru_switched_on_innodb_mon = false;
@@ -149,7 +137,7 @@ static void buf_LRU_block_free_hashed_page(buf_block_t *block)
static inline void incr_LRU_size_in_bytes(const buf_page_t* bpage)
{
/* FIXME: use atomics, not mutex */
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
buf_pool.stat.LRU_bytes += bpage->physical_size();
@@ -160,7 +148,7 @@ static inline void incr_LRU_size_in_bytes(const buf_page_t* bpage)
instead of the general LRU list */
bool buf_LRU_evict_from_unzip_LRU()
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
/* If the unzip_LRU list is empty, we can only use the LRU. */
if (UT_LIST_GET_LEN(buf_pool.unzip_LRU) == 0) {
@@ -196,280 +184,19 @@ bool buf_LRU_evict_from_unzip_LRU()
return(unzip_avg <= io_avg * BUF_LRU_IO_TO_UNZIP_FACTOR);
}
-#ifdef BTR_CUR_HASH_ADAPT
-/**
-While flushing (or removing dirty) pages from a tablespace we don't
-want to hog the CPU and resources. Release the buffer pool and block
-mutex and try to force a context switch. Then reacquire the same mutexes.
-The current page is "fixed" before the release of the mutexes and then
-"unfixed" again once we have reacquired the mutexes.
-@param[in,out] bpage current page */
-static void buf_flush_yield(buf_page_t *bpage)
-{
- mutex_exit(&buf_pool.flush_list_mutex);
- ut_ad(bpage->oldest_modification());
- ut_ad(bpage->in_file());
- ut_ad(bpage->io_fix() == BUF_IO_NONE);
- /** Make the block sticky, so that even after we release buf_pool.mutex:
- (1) it cannot be removed from the buf_pool.flush_list
- (2) bpage cannot be relocated in buf_pool
- (3) bpage->in_LRU_list cannot change
- However, bpage->LRU can change. */
- bpage->set_io_fix(BUF_IO_PIN);
- mutex_exit(&buf_pool.mutex);
-
- /* Try and force a context switch. */
- os_thread_yield();
-
- mutex_enter(&buf_pool.mutex);
- bpage->io_unfix();
- mutex_enter(&buf_pool.flush_list_mutex);
- /* Should not have been removed from the flush
- list during the yield. However, this check is
- not sufficient to catch a remove -> add. */
- ut_ad(bpage->oldest_modification());
-}
-
-/******************************************************************//**
-If we have hogged the resources for too long then release the buffer
-pool and flush list mutex and do a thread yield. Set the current page
-to "sticky" so that it is not relocated during the yield.
-@return true if yielded */
-static MY_ATTRIBUTE((warn_unused_result))
-bool
-buf_flush_try_yield(
-/*================*/
- buf_page_t* bpage, /*!< in/out: bpage to remove */
- ulint processed) /*!< in: number of pages processed */
-{
- /* Every BUF_LRU_DROP_SEARCH_SIZE iterations in the
- loop we release buf_pool.mutex to let other threads
- do their job but only if the block is not IO fixed. This
- ensures that the block stays in its position in the
- flush_list. */
-
- if (bpage != NULL
- && processed >= BUF_LRU_DROP_SEARCH_SIZE
- && bpage->io_fix() == BUF_IO_NONE) {
-
- /* Release the buf_pool.mutex
- to give the other threads a go. */
-
- buf_flush_yield(bpage);
- return(true);
- }
-
- return(false);
-}
-#endif /* BTR_CUR_HASH_ADAPT */
-
-/** Remove a single page from flush_list.
-@param[in,out] bpage buffer page to remove
-@param[in] flush whether to flush the page before removing
-@return true if page was removed. */
-static bool buf_flush_or_remove_page(buf_page_t *bpage, bool flush)
-{
- ut_ad(mutex_own(&buf_pool.mutex));
- ut_ad(mutex_own(&buf_pool.flush_list_mutex));
-
- /* bpage->id and bpage->io_fix are protected by
- buf_pool.mutex (and bpage->id additionally by hash_lock).
- It is safe to check them while holding buf_pool.mutex only. */
-
- if (bpage->io_fix() != BUF_IO_NONE) {
-
- /* We cannot remove this page during this scan
- yet; maybe the system is currently reading it
- in, or flushing the modifications to the file */
- return(false);
-
- }
-
- bool processed = false;
-
- /* We have to release the flush_list_mutex to obey the
- latching order. We are however guaranteed that the page
- will stay in the flush_list and won't be relocated because
- buf_flush_remove() and buf_flush_relocate_on_flush_list()
- need buf_pool.mutex as well. */
-
- mutex_exit(&buf_pool.flush_list_mutex);
-
- ut_ad(bpage->oldest_modification());
-
- if (!flush) {
- buf_flush_remove(bpage);
- processed = true;
- } else if (bpage->ready_for_flush()) {
- processed = buf_flush_page(bpage, IORequest::SINGLE_PAGE,
- nullptr, false);
-
- if (processed) {
- mutex_enter(&buf_pool.mutex);
- }
- }
-
- mutex_enter(&buf_pool.flush_list_mutex);
-
- ut_ad(mutex_own(&buf_pool.mutex));
-
- return(processed);
-}
-
-/** Remove all dirty pages belonging to a given tablespace when we are
-deleting the data file of that tablespace.
-The pages still remain a part of LRU and are evicted from
-the list as they age towards the tail of the LRU.
-@param[in] id tablespace identifier
-@param[in] flush whether to flush the pages before removing
-@param[in] first first page to be flushed or evicted
-@return whether all matching dirty pages were removed */
-static bool buf_flush_or_remove_pages(ulint id, bool flush, ulint first)
-{
- buf_page_t* prev;
- buf_page_t* bpage;
- ulint processed = 0;
-
- mutex_enter(&buf_pool.flush_list_mutex);
-rescan:
- bool all_freed = true;
-
- for (bpage = UT_LIST_GET_LAST(buf_pool.flush_list);
- bpage != NULL;
- bpage = prev) {
-
- ut_a(bpage->in_file());
-
- /* Save the previous link because once we free the
- page we can't rely on the links. */
-
- prev = UT_LIST_GET_PREV(list, bpage);
-
- const page_id_t bpage_id(bpage->id());
-
- if (id != bpage_id.space()) {
- /* Skip this block, because it is for a
- different tablespace. */
- } else if (bpage_id.page_no() < first) {
- /* Skip this block, because it is below the limit. */
- } else if (!buf_flush_or_remove_page(bpage, flush)) {
-
- /* Remove was unsuccessful, we have to try again
- by scanning the entire list from the end.
- This also means that we never released the
- buf_pool mutex. Therefore we can trust the prev
- pointer.
- buf_flush_or_remove_page() released the
- flush list mutex but not the buf_pool mutex.
- Therefore it is possible that a new page was
- added to the flush list. For example, in case
- where we are at the head of the flush list and
- prev == NULL. That is OK because we have the
- tablespace quiesced and no new pages for this
- space-id should enter flush_list. This is
- because the only callers of this function are
- DROP TABLE and FLUSH TABLE FOR EXPORT.
- We know that we'll have to do at least one more
- scan but we don't break out of loop here and
- try to do as much work as we can in this
- iteration. */
-
- all_freed = false;
- } else if (flush) {
-
- /* The processing was successful. And during the
- processing we have released the buf_pool mutex
- when calling buf_page_flush(). We cannot trust
- prev pointer. */
- goto rescan;
- }
-
-#ifdef BTR_CUR_HASH_ADAPT
- ++processed;
-
- /* Yield if we have hogged the CPU and mutexes for too long. */
- if (buf_flush_try_yield(prev, processed)) {
- /* Reset the batch size counter if we had to yield. */
- processed = 0;
- }
-#endif /* BTR_CUR_HASH_ADAPT */
- }
-
- mutex_exit(&buf_pool.flush_list_mutex);
-
- return(all_freed);
-}
-
-/** Remove or flush all the dirty pages that belong to a given tablespace.
-The pages will remain in the LRU list and will be evicted from the LRU list
-as they age and move towards the tail of the LRU list.
-@param[in] id tablespace identifier
-@param[in] flush whether to flush the pages before removing
-@param[in] first first page to be flushed or evicted */
-static void buf_flush_dirty_pages(ulint id, bool flush, ulint first)
-{
- mutex_enter(&buf_pool.mutex);
- while (!buf_flush_or_remove_pages(id, flush, first))
- {
- mutex_exit(&buf_pool.mutex);
- ut_d(buf_flush_validate());
- os_thread_sleep(2000);
- mutex_enter(&buf_pool.mutex);
- }
-
-#ifdef UNIV_DEBUG
- if (!first)
- {
- mutex_enter(&buf_pool.flush_list_mutex);
-
- for (buf_page_t *bpage= UT_LIST_GET_FIRST(buf_pool.flush_list); bpage;
- bpage= UT_LIST_GET_NEXT(list, bpage))
- {
- ut_ad(bpage->in_file());
- ut_ad(bpage->oldest_modification());
- ut_ad(id != bpage->id().space());
- }
-
- mutex_exit(&buf_pool.flush_list_mutex);
- }
-#endif
-
- mutex_exit(&buf_pool.mutex);
-}
-
-/** Empty the flush list for all pages belonging to a tablespace.
-@param[in] id tablespace identifier
-@param[in] flush whether to write the pages to files
-@param[in] first first page to be flushed or evicted */
-void buf_LRU_flush_or_remove_pages(ulint id, bool flush, ulint first)
-{
- /* Pages in the system tablespace must never be discarded. */
- ut_ad(id || flush);
-
- buf_flush_dirty_pages(id, flush, first);
-
- if (flush) {
- /* Ensure that all asynchronous IO is completed. */
- os_aio_wait_until_no_pending_writes();
- fil_flush(id);
- }
-}
-
/** Try to free an uncompressed page of a compressed block from the unzip
LRU list. The compressed page is preserved, and it need not be clean.
-@param[in] scan_all true=scan the whole list;
- false=scan srv_LRU_scan_depth / 2 blocks
+@param limit maximum number of blocks to scan
@return true if freed */
-static bool buf_LRU_free_from_unzip_LRU_list(bool scan_all)
+static bool buf_LRU_free_from_unzip_LRU_list(ulint limit)
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
if (!buf_LRU_evict_from_unzip_LRU()) {
return(false);
}
ulint scanned = 0;
- const ulint limit = scan_all ? ULINT_UNDEFINED : srv_LRU_scan_depth;
bool freed = false;
for (buf_block_t* block = UT_LIST_GET_LAST(buf_pool.unzip_LRU);
@@ -500,31 +227,24 @@ static bool buf_LRU_free_from_unzip_LRU_list(bool scan_all)
}
/** Try to free a clean page from the common LRU list.
-@param[in] scan_all true=scan the whole LRU list
- false=use BUF_LRU_SEARCH_SCAN_THRESHOLD
+@param limit maximum number of blocks to scan
@return whether a page was freed */
-static bool buf_LRU_free_from_common_LRU_list(bool scan_all)
+static bool buf_LRU_free_from_common_LRU_list(ulint limit)
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
ulint scanned = 0;
bool freed = false;
for (buf_page_t* bpage = buf_pool.lru_scan_itr.start();
- bpage && (scan_all || scanned < BUF_LRU_SEARCH_SCAN_THRESHOLD);
+ bpage && scanned < limit;
++scanned, bpage = buf_pool.lru_scan_itr.get()) {
buf_page_t* prev = UT_LIST_GET_PREV(LRU, bpage);
buf_pool.lru_scan_itr.set(prev);
const auto accessed = bpage->is_accessed();
- freed = bpage->ready_for_replace();
-
- if (freed) {
- freed = buf_LRU_free_page(bpage, true);
- if (!freed) {
- continue;
- }
-
+ if (!bpage->oldest_modification()
+ && buf_LRU_free_page(bpage, true)) {
if (!accessed) {
/* Keep track of pages that are evicted without
ever being accessed. This gives us a measure of
@@ -532,6 +252,7 @@ static bool buf_LRU_free_from_common_LRU_list(bool scan_all)
++buf_pool.stat.n_ra_pages_evicted;
}
+ freed = true;
break;
}
}
@@ -548,15 +269,14 @@ static bool buf_LRU_free_from_common_LRU_list(bool scan_all)
}
/** Try to free a replaceable block.
-@param[in] scan_all true=scan the whole LRU list,
- false=use BUF_LRU_SEARCH_SCAN_THRESHOLD
+@param limit maximum number of blocks to scan
@return true if found and freed */
-bool buf_LRU_scan_and_free_block(bool scan_all)
+bool buf_LRU_scan_and_free_block(ulint limit)
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
- return(buf_LRU_free_from_unzip_LRU_list(scan_all)
- || buf_LRU_free_from_common_LRU_list(scan_all));
+ return buf_LRU_free_from_unzip_LRU_list(limit) ||
+ buf_LRU_free_from_common_LRU_list(limit);
}
/** @return a buffer block from the buf_pool.free list
@@ -565,7 +285,7 @@ buf_block_t* buf_LRU_get_free_only()
{
buf_block_t* block;
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
block = reinterpret_cast<buf_block_t*>(
UT_LIST_GET_FIRST(buf_pool.free));
@@ -611,106 +331,89 @@ function will either assert or issue a warning and switch on the
status monitor. */
static void buf_LRU_check_size_of_non_data_objects()
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
- if (!recv_recovery_is_on()
- && buf_pool.curr_size == buf_pool.old_size
- && UT_LIST_GET_LEN(buf_pool.free)
- + UT_LIST_GET_LEN(buf_pool.LRU) < buf_pool.curr_size / 20) {
+ if (recv_recovery_is_on() || buf_pool.curr_size != buf_pool.old_size)
+ return;
- ib::fatal() << "Over 95 percent of the buffer pool is"
- " occupied by lock heaps"
+ const auto s= UT_LIST_GET_LEN(buf_pool.free) + UT_LIST_GET_LEN(buf_pool.LRU);
+
+ if (s < buf_pool.curr_size / 20)
+ ib::fatal() << "Over 95 percent of the buffer pool is"
+ " occupied by lock heaps"
#ifdef BTR_CUR_HASH_ADAPT
- " or the adaptive hash index!"
+ " or the adaptive hash index"
#endif /* BTR_CUR_HASH_ADAPT */
- " Check that your transactions do not set too many"
- " row locks, or review if"
- " innodb_buffer_pool_size="
- << (buf_pool.curr_size >> (20U - srv_page_size_shift))
- << "M could be bigger.";
- } else if (!recv_recovery_is_on()
- && buf_pool.curr_size == buf_pool.old_size
- && (UT_LIST_GET_LEN(buf_pool.free)
- + UT_LIST_GET_LEN(buf_pool.LRU))
- < buf_pool.curr_size / 3) {
-
- if (!buf_lru_switched_on_innodb_mon && srv_monitor_timer) {
-
- /* Over 67 % of the buffer pool is occupied by lock
- heaps or the adaptive hash index. This may be a memory
- leak! */
-
- ib::warn() << "Over 67 percent of the buffer pool is"
- " occupied by lock heaps"
+ "! Check that your transactions do not set too many"
+ " row locks, or review if innodb_buffer_pool_size="
+ << (buf_pool.curr_size >> (20U - srv_page_size_shift))
+ << "M could be bigger.";
+
+ if (s < buf_pool.curr_size / 3)
+ {
+ if (!buf_lru_switched_on_innodb_mon && srv_monitor_timer)
+ {
+ /* Over 67 % of the buffer pool is occupied by lock heaps or
+ the adaptive hash index. This may be a memory leak! */
+ ib::warn() << "Over 67 percent of the buffer pool is"
+ " occupied by lock heaps"
#ifdef BTR_CUR_HASH_ADAPT
- " or the adaptive hash index!"
+ " or the adaptive hash index"
#endif /* BTR_CUR_HASH_ADAPT */
- " Check that your transactions do not"
- " set too many row locks."
- " innodb_buffer_pool_size="
- << (buf_pool.curr_size >>
- (20U - srv_page_size_shift)) << "M."
- " Starting the InnoDB Monitor to print"
- " diagnostics.";
-
- buf_lru_switched_on_innodb_mon = true;
- srv_print_innodb_monitor = TRUE;
- srv_monitor_timer_schedule_now();
- }
-
- } else if (buf_lru_switched_on_innodb_mon) {
-
- /* Switch off the InnoDB Monitor; this is a simple way
- to stop the monitor if the situation becomes less urgent,
- but may also surprise users if the user also switched on the
- monitor! */
-
- buf_lru_switched_on_innodb_mon = false;
- srv_print_innodb_monitor = FALSE;
- }
+ "! Check that your transactions do not set too many row locks."
+ " innodb_buffer_pool_size="
+ << (buf_pool.curr_size >> (20U - srv_page_size_shift))
+ << "M. Starting the InnoDB Monitor to print diagnostics.";
+ buf_lru_switched_on_innodb_mon= true;
+ srv_print_innodb_monitor= TRUE;
+ srv_monitor_timer_schedule_now();
+ }
+ }
+ else if (buf_lru_switched_on_innodb_mon)
+ {
+ /* Switch off the InnoDB Monitor; this is a simple way to stop the
+ monitor if the situation becomes less urgent, but may also
+ surprise users who did SET GLOBAL innodb_status_output=ON earlier! */
+ buf_lru_switched_on_innodb_mon= false;
+ srv_print_innodb_monitor= FALSE;
+ }
}
-/** Get a free block from the buf_pool. The block is taken off the
-free list. If free list is empty, blocks are moved from the end of the
-LRU list to the free list.
+/** Get a block from the buf_pool.free list.
+If the list is empty, blocks will be moved from the end of buf_pool.LRU
+to buf_pool.free.
This function is called from a user thread when it needs a clean
block to read in a page. Note that we only ever get a block from
the free list. Even when we flush a page or find a page in LRU scan
we put it to free list to be used.
* iteration 0:
- * get a block from free list, success:done
+ * get a block from the buf_pool.free list, success:done
* if buf_pool.try_LRU_scan is set
- * scan LRU up to srv_LRU_scan_depth to find a clean block
- * the above will put the block on free list
+ * scan LRU up to 100 pages to free a clean block
* success:retry the free list
- * flush one dirty page from tail of LRU to disk
- * the above will put the block on free list
+ * flush up to innodb_lru_flush_size LRU blocks to data files
+ (until UT_LIST_GET_GEN(buf_pool.free) < innodb_lru_scan_depth)
+ * on buf_page_write_complete() the blocks will put on buf_pool.free list
* success: retry the free list
-* iteration 1:
- * same as iteration 0 except:
- * scan whole LRU list
- * scan LRU list even if buf_pool.try_LRU_scan is not set
-* iteration > 1:
- * same as iteration 1 but sleep 10ms
+* subsequent iterations: same as iteration 0 except:
+ * scan whole LRU list
+ * scan LRU list even if buf_pool.try_LRU_scan is not set
@param have_mutex whether buf_pool.mutex is already being held
@return the free control block, in state BUF_BLOCK_MEMORY */
buf_block_t* buf_LRU_get_free_block(bool have_mutex)
{
- buf_block_t* block = NULL;
- bool freed = false;
ulint n_iterations = 0;
ulint flush_failures = 0;
MONITOR_INC(MONITOR_LRU_GET_FREE_SEARCH);
if (have_mutex) {
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
goto got_mutex;
}
loop:
- mutex_enter(&buf_pool.mutex);
+ mysql_mutex_lock(&buf_pool.mutex);
got_mutex:
-
buf_LRU_check_size_of_non_data_objects();
DBUG_EXECUTE_IF("ib_lru_force_no_free_page",
@@ -718,49 +421,38 @@ got_mutex:
n_iterations = 21;
goto not_found;});
+retry:
/* If there is a block in the free list, take it */
- block = buf_LRU_get_free_only();
-
- if (block) {
+ if (buf_block_t* block = buf_LRU_get_free_only()) {
if (!have_mutex) {
- mutex_exit(&buf_pool.mutex);
+ mysql_mutex_unlock(&buf_pool.mutex);
}
memset(&block->page.zip, 0, sizeof block->page.zip);
- return(block);
+ return block;
}
MONITOR_INC( MONITOR_LRU_GET_FREE_LOOPS );
- freed = false;
if (n_iterations || buf_pool.try_LRU_scan) {
/* If no block was in the free list, search from the
end of the LRU list and try to free a block there.
If we are doing for the first time we'll scan only
tail of the LRU list otherwise we scan the whole LRU
list. */
- freed = buf_LRU_scan_and_free_block(n_iterations > 0);
-
- if (!freed && n_iterations == 0) {
- /* Tell other threads that there is no point
- in scanning the LRU list. This flag is set to
- TRUE again when we flush a batch from this
- buffer pool. */
- buf_pool.try_LRU_scan = false;
-
- /* Also tell the page_cleaner thread that
- there is work for it to do. */
- os_event_set(buf_flush_event);
+ if (buf_LRU_scan_and_free_block(n_iterations
+ ? ULINT_UNDEFINED : 100)) {
+ goto retry;
}
+
+ /* Tell other threads that there is no point
+ in scanning the LRU list. */
+ buf_pool.try_LRU_scan = false;
}
#ifndef DBUG_OFF
not_found:
#endif
-
- mutex_exit(&buf_pool.mutex);
-
- if (freed) {
- goto loop;
- }
+ mysql_mutex_unlock(&buf_pool.mutex);
+ buf_flush_wait_batch_end_acquiring_mutex(true);
if (n_iterations > 20 && !buf_lru_free_blocks_error_printed
&& srv_buf_pool_old_size == srv_buf_pool_size) {
@@ -782,18 +474,8 @@ not_found:
buf_lru_free_blocks_error_printed = true;
}
- /* If we have scanned the whole LRU and still are unable to
- find a free block then we should sleep here to let the
- page_cleaner do an LRU batch for us. */
-
- if (!srv_read_only_mode) {
- os_event_set(buf_flush_event);
- }
-
if (n_iterations > 1) {
-
MONITOR_INC( MONITOR_LRU_GET_FREE_WAITS );
- os_thread_sleep(10000);
}
/* No free block was found: try to flush the LRU list.
@@ -804,10 +486,10 @@ not_found:
TODO: A more elegant way would have been to return the freed
up block to the caller here but the code that deals with
removing the block from page_hash and LRU_list is fairly
- involved (particularly in case of compressed pages). We
+ involved (particularly in case of ROW_FORMAT=COMPRESSED pages). We
can do that in a separate patch sometime in future. */
- if (!buf_flush_single_page_from_LRU()) {
+ if (!buf_flush_lists(innodb_lru_flush_size, 0)) {
MONITOR_INC(MONITOR_LRU_SINGLE_FLUSH_FAILURE_COUNT);
++flush_failures;
}
@@ -827,7 +509,7 @@ static void buf_LRU_old_adjust_len()
ulint new_len;
ut_a(buf_pool.LRU_old);
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
ut_ad(buf_pool.LRU_old_ratio >= BUF_LRU_OLD_RATIO_MIN);
ut_ad(buf_pool.LRU_old_ratio <= BUF_LRU_OLD_RATIO_MAX);
compile_time_assert(BUF_LRU_OLD_RATIO_MIN * BUF_LRU_OLD_MIN_LEN
@@ -888,7 +570,7 @@ static void buf_LRU_old_adjust_len()
called when the LRU list grows to BUF_LRU_OLD_MIN_LEN length. */
static void buf_LRU_old_init()
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
ut_a(UT_LIST_GET_LEN(buf_pool.LRU) == BUF_LRU_OLD_MIN_LEN);
/* We first initialize all blocks in the LRU list as old and then use
@@ -917,7 +599,7 @@ static void buf_LRU_old_init()
static void buf_unzip_LRU_remove_block_if_needed(buf_page_t* bpage)
{
ut_ad(bpage->in_file());
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
if (bpage->belongs_to_unzip_LRU()) {
buf_block_t* block = reinterpret_cast<buf_block_t*>(bpage);
@@ -1000,7 +682,7 @@ buf_unzip_LRU_add_block(
ibool old) /*!< in: TRUE if should be put to the end
of the list, else put to the start */
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
ut_a(block->page.belongs_to_unzip_LRU());
ut_ad(!block->in_unzip_LRU_list);
ut_d(block->in_unzip_LRU_list = true);
@@ -1024,7 +706,7 @@ buf_LRU_add_block(
LRU list is very short, the block is added to
the start, regardless of this parameter */
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
ut_ad(!bpage->in_LRU_list);
if (!old || (UT_LIST_GET_LEN(buf_pool.LRU) < BUF_LRU_OLD_MIN_LEN)) {
@@ -1084,7 +766,7 @@ void buf_page_make_young(buf_page_t *bpage)
{
ut_ad(bpage->in_file());
- mutex_enter(&buf_pool.mutex);
+ mysql_mutex_lock(&buf_pool.mutex);
if (UNIV_UNLIKELY(bpage->old))
buf_pool.stat.n_pages_made_young++;
@@ -1092,7 +774,7 @@ void buf_page_make_young(buf_page_t *bpage)
buf_LRU_remove_block(bpage);
buf_LRU_add_block(bpage, false);
- mutex_exit(&buf_pool.mutex);
+ mysql_mutex_unlock(&buf_pool.mutex);
}
/** Try to free a block. If bpage is a descriptor of a compressed-only
@@ -1107,7 +789,7 @@ bool buf_LRU_free_page(buf_page_t *bpage, bool zip)
const page_id_t id(bpage->id());
buf_page_t* b = nullptr;
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
ut_ad(bpage->in_file());
ut_ad(bpage->in_LRU_list);
@@ -1148,7 +830,7 @@ func_exit:
b->set_state(BUF_BLOCK_ZIP_PAGE);
}
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
ut_ad(bpage->in_file());
ut_ad(bpage->in_LRU_list);
@@ -1238,9 +920,7 @@ func_exit:
buf_LRU_add_block(b, b->old);
}
- if (b->oldest_modification()) {
- buf_flush_relocate_on_flush_list(bpage, b);
- }
+ buf_flush_relocate_on_flush_list(bpage, b);
bpage->zip.data = nullptr;
@@ -1253,25 +933,35 @@ func_exit:
hash_lock->write_unlock();
}
- mutex_exit(&buf_pool.mutex);
-
- /* Remove possible adaptive hash index on the page.
- The page was declared uninitialized by
- buf_LRU_block_remove_hashed(). We need to flag
- the contents of the page valid (which it still is) in
- order to avoid bogus Valgrind or MSAN warnings.*/
buf_block_t* block = reinterpret_cast<buf_block_t*>(bpage);
- MEM_MAKE_DEFINED(block->frame, srv_page_size);
- btr_search_drop_page_hash_index(block);
- MEM_UNDEFINED(block->frame, srv_page_size);
+#ifdef BTR_CUR_HASH_ADAPT
+ if (block->index) {
+ mysql_mutex_unlock(&buf_pool.mutex);
+
+ /* Remove the adaptive hash index on the page.
+ The page was declared uninitialized by
+ buf_LRU_block_remove_hashed(). We need to flag
+ the contents of the page valid (which it still is) in
+ order to avoid bogus Valgrind or MSAN warnings.*/
+
+ MEM_MAKE_DEFINED(block->frame, srv_page_size);
+ btr_search_drop_page_hash_index(block);
+ MEM_UNDEFINED(block->frame, srv_page_size);
+
+ if (UNIV_LIKELY_NULL(b)) {
+ ut_ad(b->zip_size());
+ b->io_unfix();
+ }
+ mysql_mutex_lock(&buf_pool.mutex);
+ } else
+#endif
if (UNIV_LIKELY_NULL(b)) {
ut_ad(b->zip_size());
b->io_unfix();
}
- mutex_enter(&buf_pool.mutex);
buf_LRU_block_free_hashed_page(block);
return(true);
@@ -1332,6 +1022,16 @@ buf_LRU_block_free_non_file_page(
MEM_NOACCESS(block->frame, srv_page_size);
}
+/** Release a memory block to the buffer pool. */
+ATTRIBUTE_COLD void buf_pool_t::free_block(buf_block_t *block)
+{
+ ut_ad(this == &buf_pool);
+ mysql_mutex_lock(&mutex);
+ buf_LRU_block_free_non_file_page(block);
+ mysql_mutex_unlock(&mutex);
+}
+
+
/** Remove bpage from buf_pool.LRU and buf_pool.page_hash.
If bpage->state() == BUF_BLOCK_ZIP_PAGE && !bpage->oldest_modification(),
@@ -1350,7 +1050,7 @@ this case the block is already returned to the buddy allocator. */
static bool buf_LRU_block_remove_hashed(buf_page_t *bpage, const page_id_t id,
page_hash_latch *hash_lock, bool zip)
{
- ut_ad(mutex_own(&buf_pool.mutex));
+ mysql_mutex_assert_owner(&buf_pool.mutex);
ut_ad(hash_lock->is_write_locked());
ut_a(bpage->io_fix() == BUF_IO_NONE);
@@ -1545,7 +1245,7 @@ uint buf_LRU_old_ratio_update(uint old_pct, bool adjust)
}
if (adjust) {
- mutex_enter(&buf_pool.mutex);
+ mysql_mutex_lock(&buf_pool.mutex);
if (ratio != buf_pool.LRU_old_ratio) {
buf_pool.LRU_old_ratio = ratio;
@@ -1556,7 +1256,7 @@ uint buf_LRU_old_ratio_update(uint old_pct, bool adjust)
}
}
- mutex_exit(&buf_pool.mutex);
+ mysql_mutex_unlock(&buf_pool.mutex);
} else {
buf_pool.LRU_old_ratio = ratio;
}
@@ -1609,7 +1309,7 @@ void buf_LRU_validate()
ulint old_len;
ulint new_len;
- mutex_enter(&buf_pool.mutex);
+ mysql_mutex_lock(&buf_pool.mutex);
if (UT_LIST_GET_LEN(buf_pool.LRU) >= BUF_LRU_OLD_MIN_LEN) {
@@ -1687,7 +1387,7 @@ void buf_LRU_validate()
ut_a(block->page.belongs_to_unzip_LRU());
}
- mutex_exit(&buf_pool.mutex);
+ mysql_mutex_unlock(&buf_pool.mutex);
}
#endif /* UNIV_DEBUG */
@@ -1695,7 +1395,7 @@ void buf_LRU_validate()
/** Dump the LRU list to stderr. */
void buf_LRU_print()
{
- mutex_enter(&buf_pool.mutex);
+ mysql_mutex_lock(&buf_pool.mutex);
for (buf_page_t* bpage = UT_LIST_GET_FIRST(buf_pool.LRU);
bpage != NULL;
@@ -1744,6 +1444,6 @@ void buf_LRU_print()
}
}
- mutex_exit(&buf_pool.mutex);
+ mysql_mutex_unlock(&buf_pool.mutex);
}
#endif /* UNIV_DEBUG_PRINT || UNIV_DEBUG */