diff options
Diffstat (limited to 'storage/xtradb/buf/buf0lru.c')
-rw-r--r-- | storage/xtradb/buf/buf0lru.c | 2210 |
1 files changed, 2210 insertions, 0 deletions
diff --git a/storage/xtradb/buf/buf0lru.c b/storage/xtradb/buf/buf0lru.c new file mode 100644 index 00000000000..be6acb83d09 --- /dev/null +++ b/storage/xtradb/buf/buf0lru.c @@ -0,0 +1,2210 @@ +/***************************************************************************** + +Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA + +*****************************************************************************/ + +/****************************************************** +The database buffer replacement algorithm + +Created 11/5/1995 Heikki Tuuri +*******************************************************/ + +#include "buf0lru.h" + +#ifdef UNIV_NONINL +#include "buf0lru.ic" +#endif + +#include "ut0byte.h" +#include "ut0lst.h" +#include "ut0rnd.h" +#include "sync0sync.h" +#include "sync0rw.h" +#include "hash0hash.h" +#include "os0sync.h" +#include "fil0fil.h" +#include "btr0btr.h" +#include "buf0buddy.h" +#include "buf0buf.h" +#include "buf0flu.h" +#include "buf0rea.h" +#include "btr0sea.h" +#include "ibuf0ibuf.h" +#include "os0file.h" +#include "page0zip.h" +#include "log0recv.h" +#include "srv0srv.h" + +/* The number of blocks from the LRU_old pointer onward, including the block +pointed to, must be 3/8 of the whole LRU list length, except that the +tolerance defined below 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. */ + +#define BUF_LRU_OLD_TOLERANCE 20 + +/* The whole LRU list length is divided by this number to determine an +initial segment in buf_LRU_get_recent_limit */ + +#define BUF_LRU_INITIAL_RATIO 8 + +/* 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. */ +#define BUF_LRU_DROP_SEARCH_HASH_SIZE 1024 + +/* If we switch on the InnoDB monitor because there are too few available +frames in the buffer pool, we set this to TRUE */ +UNIV_INTERN ibool buf_lru_switched_on_innodb_mon = FALSE; + +/********************************************************************** +These statistics are not 'of' LRU but 'for' LRU. We keep count of I/O +and page_zip_decompress() operations. Based on the statistics, +buf_LRU_evict_from_unzip_LRU() decides if we want to evict from +unzip_LRU or the regular LRU. From unzip_LRU, we will only evict the +uncompressed frame (meaning we can evict dirty blocks as well). From +the regular LRU, we will evict the entire block (i.e.: both the +uncompressed and compressed data), which must be clean. */ + +/* Number of intervals for which we keep the history of these stats. +Each interval is 1 second, defined by the rate at which +srv_error_monitor_thread() calls buf_LRU_stat_update(). */ +#define BUF_LRU_STAT_N_INTERVAL 50 + +/* Co-efficient with which we multiply I/O operations to equate them +with page_zip_decompress() operations. */ +#define BUF_LRU_IO_TO_UNZIP_FACTOR 50 + +/* Sampled values buf_LRU_stat_cur. +Protected by buf_pool_mutex. Updated by buf_LRU_stat_update(). */ +static buf_LRU_stat_t buf_LRU_stat_arr[BUF_LRU_STAT_N_INTERVAL]; +/* Cursor to buf_LRU_stat_arr[] that is updated in a round-robin fashion. */ +static ulint buf_LRU_stat_arr_ind; + +/* Current operation counters. Not protected by any mutex. Cleared +by buf_LRU_stat_update(). */ +UNIV_INTERN buf_LRU_stat_t buf_LRU_stat_cur; + +/* Running sum of past values of buf_LRU_stat_cur. +Updated by buf_LRU_stat_update(). Protected by buf_pool_mutex. */ +UNIV_INTERN buf_LRU_stat_t buf_LRU_stat_sum; + +/********************************************************************** +Takes a block out of the LRU list and page hash table. +If the block is compressed-only (BUF_BLOCK_ZIP_PAGE), +the object will be freed and buf_pool_zip_mutex will be released. + +If a compressed page or a compressed-only block descriptor is freed, +other compressed pages or compressed-only block descriptors may be +relocated. */ +static +enum buf_page_state +buf_LRU_block_remove_hashed_page( +/*=============================*/ + /* out: the new state of the block + (BUF_BLOCK_ZIP_FREE if the state was + BUF_BLOCK_ZIP_PAGE, or BUF_BLOCK_REMOVE_HASH + otherwise) */ + buf_page_t* bpage, /* in: block, must contain a file page and + be in a state where it can be freed; there + may or may not be a hash index to the page */ + ibool zip); /* in: TRUE if should remove also the + compressed page of an uncompressed page */ +/********************************************************************** +Puts a file page whose has no hash index to the free list. */ +static +void +buf_LRU_block_free_hashed_page( +/*===========================*/ + buf_block_t* block, /* in: block, must contain a file page and + be in a state where it can be freed */ + ibool have_page_hash_mutex); + +/********************************************************************** +Determines if the unzip_LRU list should be used for evicting a victim +instead of the general LRU list. */ +UNIV_INLINE +ibool +buf_LRU_evict_from_unzip_LRU( + ibool have_LRU_mutex) +/*==============================*/ + /* out: TRUE if should use unzip_LRU */ +{ + ulint io_avg; + ulint unzip_avg; + + //ut_ad(buf_pool_mutex_own()); + + if (!have_LRU_mutex) + mutex_enter(&LRU_list_mutex); + /* If the unzip_LRU list is empty, we can only use the LRU. */ + if (UT_LIST_GET_LEN(buf_pool->unzip_LRU) == 0) { + if (!have_LRU_mutex) + mutex_exit(&LRU_list_mutex); + return(FALSE); + } + + /* If unzip_LRU is at most 10% of the size of the LRU list, + then use the LRU. This slack allows us to keep hot + decompressed pages in the buffer pool. */ + if (UT_LIST_GET_LEN(buf_pool->unzip_LRU) + <= UT_LIST_GET_LEN(buf_pool->LRU) / 10) { + if (!have_LRU_mutex) + mutex_exit(&LRU_list_mutex); + return(FALSE); + } + + /* If eviction hasn't started yet, we assume by default + that a workload is disk bound. */ + if (buf_pool->freed_page_clock == 0) { + if (!have_LRU_mutex) + mutex_exit(&LRU_list_mutex); + return(TRUE); + } + if (!have_LRU_mutex) + mutex_exit(&LRU_list_mutex); + + /* Calculate the average over past intervals, and add the values + of the current interval. */ + io_avg = buf_LRU_stat_sum.io / BUF_LRU_STAT_N_INTERVAL + + buf_LRU_stat_cur.io; + unzip_avg = buf_LRU_stat_sum.unzip / BUF_LRU_STAT_N_INTERVAL + + buf_LRU_stat_cur.unzip; + + /* Decide based on our formula. If the load is I/O bound + (unzip_avg is smaller than the weighted io_avg), evict an + uncompressed frame from unzip_LRU. Otherwise we assume that + the load is CPU bound and evict from the regular LRU. */ + return(unzip_avg <= io_avg * BUF_LRU_IO_TO_UNZIP_FACTOR); +} + +/********************************************************************** +Attempts to drop page hash index on a batch of pages belonging to a +particular space id. */ +static +void +buf_LRU_drop_page_hash_batch( +/*=========================*/ + ulint space_id, /* in: space id */ + ulint zip_size, /* in: compressed page size in bytes + or 0 for uncompressed pages */ + const ulint* arr, /* in: array of page_no */ + ulint count) /* in: number of entries in array */ +{ + ulint i; + + ut_ad(arr != NULL); + ut_ad(count <= BUF_LRU_DROP_SEARCH_HASH_SIZE); + + for (i = 0; i < count; ++i) { + btr_search_drop_page_hash_when_freed(space_id, zip_size, + arr[i]); + } +} + +/********************************************************************** +When doing a DROP TABLE/DISCARD TABLESPACE we have to drop all page +hash index entries belonging to that table. This function tries to +do that in batch. Note that this is a 'best effort' attempt and does +not guarantee that ALL hash entries will be removed. */ +static +void +buf_LRU_drop_page_hash_for_tablespace( +/*==================================*/ + ulint id) /* in: space id */ +{ + buf_page_t* bpage; + ulint* page_arr; + ulint num_entries; + ulint zip_size; + + zip_size = fil_space_get_zip_size(id); + + if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) { + /* Somehow, the tablespace does not exist. Nothing to drop. */ + ut_ad(0); + return; + } + + page_arr = ut_malloc(sizeof(ulint) + * BUF_LRU_DROP_SEARCH_HASH_SIZE); + //buf_pool_mutex_enter(); + mutex_enter(&LRU_list_mutex); + +scan_again: + num_entries = 0; + bpage = UT_LIST_GET_LAST(buf_pool->LRU); + + while (bpage != NULL) { + mutex_t* block_mutex = buf_page_get_mutex(bpage); + buf_page_t* prev_bpage; + + mutex_enter(block_mutex); + prev_bpage = UT_LIST_GET_PREV(LRU, bpage); + + ut_a(buf_page_in_file(bpage)); + + if (buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE + || bpage->space != id + || bpage->buf_fix_count > 0 + || bpage->io_fix != BUF_IO_NONE) { + /* We leave the fixed pages as is in this scan. + To be dealt with later in the final scan. */ + mutex_exit(block_mutex); + goto next_page; + } + + if (((buf_block_t*) bpage)->is_hashed) { + + /* Store the offset(i.e.: page_no) in the array + so that we can drop hash index in a batch + later. */ + page_arr[num_entries] = bpage->offset; + mutex_exit(block_mutex); + ut_a(num_entries < BUF_LRU_DROP_SEARCH_HASH_SIZE); + ++num_entries; + + if (num_entries < BUF_LRU_DROP_SEARCH_HASH_SIZE) { + goto next_page; + } + /* Array full. We release the buf_pool_mutex to + obey the latching order. */ + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + + buf_LRU_drop_page_hash_batch(id, zip_size, page_arr, + num_entries); + num_entries = 0; + //buf_pool_mutex_enter(); + mutex_enter(&LRU_list_mutex); + } else { + mutex_exit(block_mutex); + } + +next_page: + /* Note that we may have released the buf_pool mutex + above after reading the prev_bpage during processing + of a page_hash_batch (i.e.: when the array was full). + This means that prev_bpage can change in LRU list. + This is OK because this function is a 'best effort' + to drop as many search hash entries as possible and + it does not guarantee that ALL such entries will be + dropped. */ + bpage = prev_bpage; + + /* If, however, bpage has been removed from LRU list + to the free list then we should restart the scan. + bpage->state is protected by buf_pool mutex. */ + if (bpage && !buf_page_in_file(bpage)) { + ut_a(num_entries == 0); + goto scan_again; + } + } + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + + /* Drop any remaining batch of search hashed pages. */ + buf_LRU_drop_page_hash_batch(id, zip_size, page_arr, num_entries); + ut_free(page_arr); +} + +/********************************************************************** +Invalidates all pages belonging to a given tablespace when we are deleting +the data file(s) of that tablespace. */ +UNIV_INTERN +void +buf_LRU_invalidate_tablespace( +/*==========================*/ + ulint id) /* in: space id */ +{ + buf_page_t* bpage; + ibool all_freed; + + /* Before we attempt to drop pages one by one we first + attempt to drop page hash index entries in batches to make + it more efficient. The batching attempt is a best effort + attempt and does not guarantee that all pages hash entries + will be dropped. We get rid of remaining page hash entries + one by one below. */ + buf_LRU_drop_page_hash_for_tablespace(id); + +scan_again: + //buf_pool_mutex_enter(); + mutex_enter(&LRU_list_mutex); + mutex_enter(&flush_list_mutex); + rw_lock_x_lock(&page_hash_latch); + + all_freed = TRUE; + + bpage = UT_LIST_GET_LAST(buf_pool->LRU); + + while (bpage != NULL) { + mutex_t* block_mutex = buf_page_get_mutex(bpage); + buf_page_t* prev_bpage; + + ut_a(buf_page_in_file(bpage)); + + mutex_enter(block_mutex); + prev_bpage = UT_LIST_GET_PREV(LRU, bpage); + + if (buf_page_get_space(bpage) == id) { + if (bpage->buf_fix_count > 0 + || buf_page_get_io_fix(bpage) != 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 */ + + all_freed = FALSE; + + goto next_page; + } + +#ifdef UNIV_DEBUG + if (buf_debug_prints) { + fprintf(stderr, + "Dropping space %lu page %lu\n", + (ulong) buf_page_get_space(bpage), + (ulong) buf_page_get_page_no(bpage)); + } +#endif + if (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE + && ((buf_block_t*) bpage)->is_hashed) { + ulint page_no; + ulint zip_size; + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + mutex_exit(&flush_list_mutex); + rw_lock_x_unlock(&page_hash_latch); + + zip_size = buf_page_get_zip_size(bpage); + page_no = buf_page_get_page_no(bpage); + + mutex_exit(block_mutex); + + /* Note that the following call will acquire + an S-latch on the page */ + + btr_search_drop_page_hash_when_freed( + id, zip_size, page_no); + goto scan_again; + } + + if (bpage->oldest_modification != 0) { + + buf_flush_remove(bpage); + } + + /* Remove from the LRU list */ + if (buf_LRU_block_remove_hashed_page(bpage, TRUE) + != BUF_BLOCK_ZIP_FREE) { + buf_LRU_block_free_hashed_page((buf_block_t*) + bpage, TRUE); + } else { + /* The block_mutex should have been + released by buf_LRU_block_remove_hashed_page() + when it returns BUF_BLOCK_ZIP_FREE. */ + ut_ad(block_mutex == &buf_pool_zip_mutex); + ut_ad(!mutex_own(block_mutex)); + + /* The compressed block descriptor + (bpage) has been deallocated and + block_mutex released. Also, + buf_buddy_free() may have relocated + prev_bpage. Rescan the LRU list. */ + + bpage = UT_LIST_GET_LAST(buf_pool->LRU); + continue; + } + } +next_page: + mutex_exit(block_mutex); + bpage = prev_bpage; + } + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + mutex_exit(&flush_list_mutex); + rw_lock_x_unlock(&page_hash_latch); + + if (!all_freed) { + os_thread_sleep(20000); + + goto scan_again; + } +} + +/********************************************************************** +Gets the minimum LRU_position field for the blocks in an initial segment +(determined by BUF_LRU_INITIAL_RATIO) of the LRU list. The limit is not +guaranteed to be precise, because the ulint_clock may wrap around. */ +UNIV_INTERN +ulint +buf_LRU_get_recent_limit(void) +/*==========================*/ + /* out: the limit; zero if could not determine it */ +{ + const buf_page_t* bpage; + ulint len; + ulint limit; + + //buf_pool_mutex_enter(); + mutex_enter(&LRU_list_mutex); + + len = UT_LIST_GET_LEN(buf_pool->LRU); + + if (len < BUF_LRU_OLD_MIN_LEN) { + /* The LRU list is too short to do read-ahead */ + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + + return(0); + } + + bpage = UT_LIST_GET_FIRST(buf_pool->LRU); + + limit = buf_page_get_LRU_position(bpage) - len / BUF_LRU_INITIAL_RATIO; + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + + return(limit); +} + +/************************************************************************ +Insert a compressed block into buf_pool->zip_clean in the LRU order. */ +UNIV_INTERN +void +buf_LRU_insert_zip_clean( +/*=====================*/ + buf_page_t* bpage) /* in: pointer to the block in question */ +{ + buf_page_t* b; + + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&flush_list_mutex)); + ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_PAGE); + + /* Find the first successor of bpage in the LRU list + that is in the zip_clean list. */ + b = bpage; + do { + b = UT_LIST_GET_NEXT(LRU, b); + } while (b && (buf_page_get_state(b) != BUF_BLOCK_ZIP_PAGE || !b->in_LRU_list)); + + /* Insert bpage before b, i.e., after the predecessor of b. */ + if (b) { + b = UT_LIST_GET_PREV(list, b); + } + + if (b) { + UT_LIST_INSERT_AFTER(list, buf_pool->zip_clean, b, bpage); + } else { + UT_LIST_ADD_FIRST(list, buf_pool->zip_clean, bpage); + } +} + +/********************************************************************** +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. */ +UNIV_INLINE +ibool +buf_LRU_free_from_unzip_LRU_list( +/*=============================*/ + /* out: TRUE if freed */ + ulint n_iterations, /* in: how many times this has been called + repeatedly without result: a high value means + that we should search farther; we will search + n_iterations / 5 of the unzip_LRU list, + or nothing if n_iterations >= 5 */ + ibool have_LRU_mutex) +{ + buf_block_t* block; + ulint distance; + + //ut_ad(buf_pool_mutex_own()); /* optimistic */ + + /* Theoratically it should be much easier to find a victim + from unzip_LRU as we can choose even a dirty block (as we'll + be evicting only the uncompressed frame). In a very unlikely + eventuality that we are unable to find a victim from + unzip_LRU, we fall back to the regular LRU list. We do this + if we have done five iterations so far. */ + + if (UNIV_UNLIKELY(n_iterations >= 5) + || !buf_LRU_evict_from_unzip_LRU(have_LRU_mutex)) { + + return(FALSE); + } + + distance = 100 + (n_iterations + * UT_LIST_GET_LEN(buf_pool->unzip_LRU)) / 5; + +restart: + for (block = UT_LIST_GET_LAST(buf_pool->unzip_LRU); + UNIV_LIKELY(block != NULL) && UNIV_LIKELY(distance > 0); + block = UT_LIST_GET_PREV(unzip_LRU, block), distance--) { + if (!block->in_unzip_LRU_list || !block->page.in_LRU_list + || buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE) + goto restart; + + enum buf_lru_free_block_status freed; + + /* optimistic */ + //ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); + //ut_ad(block->in_unzip_LRU_list); + //ut_ad(block->page.in_LRU_list); + + mutex_enter(&block->mutex); + freed = buf_LRU_free_block(&block->page, FALSE, NULL, have_LRU_mutex); + mutex_exit(&block->mutex); + + switch (freed) { + case BUF_LRU_FREED: + return(TRUE); + + case BUF_LRU_CANNOT_RELOCATE: + /* If we failed to relocate, try + regular LRU eviction. */ + return(FALSE); + + case BUF_LRU_NOT_FREED: + /* The block was buffer-fixed or I/O-fixed. + Keep looking. */ + continue; + } + + /* inappropriate return value from + buf_LRU_free_block() */ + ut_error; + } + + return(FALSE); +} + +/********************************************************************** +Try to free a clean page from the common LRU list. */ +UNIV_INLINE +ibool +buf_LRU_free_from_common_LRU_list( +/*==============================*/ + /* out: TRUE if freed */ + ulint n_iterations, /* in: how many times this has been called + repeatedly without result: a high value means + that we should search farther; if + n_iterations < 10, then we search + n_iterations / 10 * buf_pool->curr_size + pages from the end of the LRU list */ + ibool have_LRU_mutex) +{ + buf_page_t* bpage; + ulint distance; + + //ut_ad(buf_pool_mutex_own()); /* optimistic */ + + distance = 100 + (n_iterations * buf_pool->curr_size) / 10; + +restart: + for (bpage = UT_LIST_GET_LAST(buf_pool->LRU); + UNIV_LIKELY(bpage != NULL) && UNIV_LIKELY(distance > 0); + bpage = UT_LIST_GET_PREV(LRU, bpage), distance--) { + if (!bpage->in_LRU_list + || buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE) + goto restart; + + enum buf_lru_free_block_status freed; + mutex_t* block_mutex + = buf_page_get_mutex(bpage); + + /* optimistic */ + //ut_ad(buf_page_in_file(bpage)); + //ut_ad(bpage->in_LRU_list); + + mutex_enter(block_mutex); + freed = buf_LRU_free_block(bpage, TRUE, NULL, have_LRU_mutex); + mutex_exit(block_mutex); + + switch (freed) { + case BUF_LRU_FREED: + return(TRUE); + + case BUF_LRU_NOT_FREED: + /* The block was dirty, buffer-fixed, or I/O-fixed. + Keep looking. */ + continue; + + case BUF_LRU_CANNOT_RELOCATE: + /* This should never occur, because we + want to discard the compressed page too. */ + break; + } + + /* inappropriate return value from + buf_LRU_free_block() */ + ut_error; + } + + return(FALSE); +} + +/********************************************************************** +Try to free a replaceable block. */ +UNIV_INTERN +ibool +buf_LRU_search_and_free_block( +/*==========================*/ + /* out: TRUE if found and freed */ + ulint n_iterations) /* in: how many times this has been called + repeatedly without result: a high value means + that we should search farther; if + n_iterations < 10, then we search + n_iterations / 10 * buf_pool->curr_size + pages from the end of the LRU list; if + n_iterations < 5, then we will also search + n_iterations / 5 of the unzip_LRU list. */ +{ + ibool freed = FALSE; + ibool have_LRU_mutex = FALSE; + + if (UT_LIST_GET_LEN(buf_pool->unzip_LRU)) + have_LRU_mutex = TRUE; + + /* optimistic search... */ + //buf_pool_mutex_enter(); + if (have_LRU_mutex) + mutex_enter(&LRU_list_mutex); + + freed = buf_LRU_free_from_unzip_LRU_list(n_iterations, have_LRU_mutex); + + if (!freed) { + freed = buf_LRU_free_from_common_LRU_list(n_iterations, have_LRU_mutex); + } + + mutex_enter(&buf_pool_mutex); + if (!freed) { + buf_pool->LRU_flush_ended = 0; + } else if (buf_pool->LRU_flush_ended > 0) { + buf_pool->LRU_flush_ended--; + } + mutex_exit(&buf_pool_mutex); + + //buf_pool_mutex_exit(); + if (have_LRU_mutex) + mutex_exit(&LRU_list_mutex); + + return(freed); +} + +/********************************************************************** +Tries to remove LRU flushed blocks from the end of the LRU list and put them +to the free list. This is beneficial for the efficiency of the insert buffer +operation, as flushed pages from non-unique non-clustered indexes are here +taken out of the buffer pool, and their inserts redirected to the insert +buffer. Otherwise, the flushed blocks could get modified again before read +operations need new buffer blocks, and the i/o work done in flushing would be +wasted. */ +UNIV_INTERN +void +buf_LRU_try_free_flushed_blocks(void) +/*=================================*/ +{ + //buf_pool_mutex_enter(); + mutex_enter(&buf_pool_mutex); + + while (buf_pool->LRU_flush_ended > 0) { + + //buf_pool_mutex_exit(); + mutex_exit(&buf_pool_mutex); + + buf_LRU_search_and_free_block(1); + + //buf_pool_mutex_enter(); + mutex_enter(&buf_pool_mutex); + } + + //buf_pool_mutex_exit(); + mutex_exit(&buf_pool_mutex); +} + +/********************************************************************** +Returns TRUE if less than 25 % of the buffer pool is available. This can be +used in heuristics to prevent huge transactions eating up the whole buffer +pool for their locks. */ +UNIV_INTERN +ibool +buf_LRU_buf_pool_running_out(void) +/*==============================*/ + /* out: TRUE if less than 25 % of buffer pool + left */ +{ + ibool ret = FALSE; + + //buf_pool_mutex_enter(); + mutex_enter(&LRU_list_mutex); + mutex_enter(&free_list_mutex); + + if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free) + + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->curr_size / 4) { + + ret = TRUE; + } + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + mutex_exit(&free_list_mutex); + + return(ret); +} + +/********************************************************************** +Returns a free block from the buf_pool. The block is taken off the +free list. If it is empty, returns NULL. */ +UNIV_INTERN +buf_block_t* +buf_LRU_get_free_only(void) +/*=======================*/ + /* out: a free control block, or NULL + if the buf_block->free list is empty */ +{ + buf_block_t* block; + + //ut_ad(buf_pool_mutex_own()); + + mutex_enter(&free_list_mutex); + block = (buf_block_t*) UT_LIST_GET_LAST(buf_pool->free); + + if (block) { + ut_ad(block->page.in_free_list); + ut_d(block->page.in_free_list = FALSE); + ut_ad(!block->page.in_flush_list); + ut_ad(!block->page.in_LRU_list); + ut_a(!buf_page_in_file(&block->page)); + UT_LIST_REMOVE(list, buf_pool->free, (&block->page)); + + mutex_exit(&free_list_mutex); + + mutex_enter(&block->mutex); + + buf_block_set_state(block, BUF_BLOCK_READY_FOR_USE); + UNIV_MEM_ALLOC(block->frame, UNIV_PAGE_SIZE); + + mutex_exit(&block->mutex); + } else { + mutex_exit(&free_list_mutex); + } + + return(block); +} + +/********************************************************************** +Returns a free block from the buf_pool. The block is taken off the +free list. If it is empty, blocks are moved from the end of the +LRU list to the free list. */ +UNIV_INTERN +buf_block_t* +buf_LRU_get_free_block( +/*===================*/ + /* out: the free control block, + in state BUF_BLOCK_READY_FOR_USE */ + ulint zip_size) /* in: compressed page size in bytes, + or 0 if uncompressed tablespace */ +{ + buf_block_t* block = NULL; + ibool freed; + ulint n_iterations = 1; + ibool mon_value_was = FALSE; + ibool started_monitor = FALSE; +loop: + //buf_pool_mutex_enter(); + + if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free) + + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->curr_size / 20) { + ut_print_timestamp(stderr); + + fprintf(stderr, + " InnoDB: ERROR: over 95 percent of the buffer pool" + " is occupied by\n" + "InnoDB: lock heaps or the adaptive hash index!" + " Check that your\n" + "InnoDB: transactions do not set too many row locks.\n" + "InnoDB: Your buffer pool size is %lu MB." + " Maybe you should make\n" + "InnoDB: the buffer pool bigger?\n" + "InnoDB: We intentionally generate a seg fault" + " to print a stack trace\n" + "InnoDB: on Linux!\n", + (ulong) (buf_pool->curr_size + / (1024 * 1024 / UNIV_PAGE_SIZE))); + + ut_error; + + } else if (!recv_recovery_on + && (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) { + + /* Over 67 % of the buffer pool is occupied by lock + heaps or the adaptive hash index. This may be a memory + leak! */ + + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: WARNING: over 67 percent of" + " the buffer pool is occupied by\n" + "InnoDB: lock heaps or the adaptive" + " hash index! Check that your\n" + "InnoDB: transactions do not set too many" + " row locks.\n" + "InnoDB: Your buffer pool size is %lu MB." + " Maybe you should make\n" + "InnoDB: the buffer pool bigger?\n" + "InnoDB: Starting the InnoDB Monitor to print" + " diagnostics, including\n" + "InnoDB: lock heap and hash index sizes.\n", + (ulong) (buf_pool->curr_size + / (1024 * 1024 / UNIV_PAGE_SIZE))); + + buf_lru_switched_on_innodb_mon = TRUE; + srv_print_innodb_monitor = TRUE; + os_event_set(srv_lock_timeout_thread_event); + } + } 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; + } + + /* If there is a block in the free list, take it */ + block = buf_LRU_get_free_only(); + if (block) { + +#ifdef UNIV_DEBUG + block->page.zip.m_start = +#endif /* UNIV_DEBUG */ + block->page.zip.m_end = + block->page.zip.m_nonempty = + block->page.zip.n_blobs = 0; + + if (UNIV_UNLIKELY(zip_size)) { + ibool lru; + page_zip_set_size(&block->page.zip, zip_size); + mutex_enter(&LRU_list_mutex); + block->page.zip.data = buf_buddy_alloc(zip_size, &lru, FALSE); + mutex_exit(&LRU_list_mutex); + UNIV_MEM_DESC(block->page.zip.data, zip_size, block); + } else { + page_zip_set_size(&block->page.zip, 0); + block->page.zip.data = NULL; + } + + //buf_pool_mutex_exit(); + + if (started_monitor) { + srv_print_innodb_monitor = mon_value_was; + } + + return(block); + } + + /* If no block was in the free list, search from the end of the LRU + list and try to free a block there */ + + //buf_pool_mutex_exit(); + + freed = buf_LRU_search_and_free_block(n_iterations); + + if (freed > 0) { + goto loop; + } + + if (n_iterations > 30) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Warning: difficult to find free blocks in\n" + "InnoDB: the buffer pool (%lu search iterations)!" + " Consider\n" + "InnoDB: increasing the buffer pool size.\n" + "InnoDB: It is also possible that" + " in your Unix version\n" + "InnoDB: fsync is very slow, or" + " completely frozen inside\n" + "InnoDB: the OS kernel. Then upgrading to" + " a newer version\n" + "InnoDB: of your operating system may help." + " Look at the\n" + "InnoDB: number of fsyncs in diagnostic info below.\n" + "InnoDB: Pending flushes (fsync) log: %lu;" + " buffer pool: %lu\n" + "InnoDB: %lu OS file reads, %lu OS file writes," + " %lu OS fsyncs\n" + "InnoDB: Starting InnoDB Monitor to print further\n" + "InnoDB: diagnostics to the standard output.\n", + (ulong) n_iterations, + (ulong) fil_n_pending_log_flushes, + (ulong) fil_n_pending_tablespace_flushes, + (ulong) os_n_file_reads, (ulong) os_n_file_writes, + (ulong) os_n_fsyncs); + + mon_value_was = srv_print_innodb_monitor; + started_monitor = TRUE; + srv_print_innodb_monitor = TRUE; + os_event_set(srv_lock_timeout_thread_event); + } + + /* No free block was found: try to flush the LRU list */ + + buf_flush_free_margin(TRUE); + ++srv_buf_pool_wait_free; + + os_aio_simulated_wake_handler_threads(); + + //buf_pool_mutex_enter(); + mutex_enter(&buf_pool_mutex); + + if (buf_pool->LRU_flush_ended > 0) { + /* We have written pages in an LRU flush. To make the insert + buffer more efficient, we try to move these pages to the free + list. */ + + //buf_pool_mutex_exit(); + mutex_exit(&buf_pool_mutex); + + buf_LRU_try_free_flushed_blocks(); + } else { + //buf_pool_mutex_exit(); + mutex_exit(&buf_pool_mutex); + } + + if (n_iterations > 10) { + + os_thread_sleep(500000); + } + + n_iterations++; + + goto loop; +} + +/*********************************************************************** +Moves the LRU_old pointer so that the length of the old blocks list +is inside the allowed limits. */ +UNIV_INLINE +void +buf_LRU_old_adjust_len(void) +/*========================*/ +{ + ulint old_len; + ulint new_len; + + ut_a(buf_pool->LRU_old); + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&LRU_list_mutex)); +#if 3 * (BUF_LRU_OLD_MIN_LEN / 8) <= BUF_LRU_OLD_TOLERANCE + 5 +# error "3 * (BUF_LRU_OLD_MIN_LEN / 8) <= BUF_LRU_OLD_TOLERANCE + 5" +#endif +#ifdef UNIV_LRU_DEBUG + /* buf_pool->LRU_old must be the first item in the LRU list + whose "old" flag is set. */ + ut_a(buf_pool->LRU_old->old); + ut_a(!UT_LIST_GET_PREV(LRU, buf_pool->LRU_old) + || !UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)->old); + ut_a(!UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old) + || UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)->old); +#endif /* UNIV_LRU_DEBUG */ + + for (;;) { + old_len = buf_pool->LRU_old_len; + new_len = 3 * (UT_LIST_GET_LEN(buf_pool->LRU) / 8); + + ut_ad(buf_pool->LRU_old->in_LRU_list); + ut_a(buf_pool->LRU_old); +#ifdef UNIV_LRU_DEBUG + ut_a(buf_pool->LRU_old->old); +#endif /* UNIV_LRU_DEBUG */ + + /* Update the LRU_old pointer if necessary */ + + if (old_len < new_len - BUF_LRU_OLD_TOLERANCE) { + + buf_pool->LRU_old = UT_LIST_GET_PREV( + LRU, buf_pool->LRU_old); +#ifdef UNIV_LRU_DEBUG + ut_a(!buf_pool->LRU_old->old); +#endif /* UNIV_LRU_DEBUG */ + buf_page_set_old(buf_pool->LRU_old, TRUE); + buf_pool->LRU_old_len++; + + } else if (old_len > new_len + BUF_LRU_OLD_TOLERANCE) { + + buf_page_set_old(buf_pool->LRU_old, FALSE); + buf_pool->LRU_old = UT_LIST_GET_NEXT( + LRU, buf_pool->LRU_old); + buf_pool->LRU_old_len--; + } else { + return; + } + } +} + +/*********************************************************************** +Initializes the old blocks pointer in the LRU list. This function should be +called when the LRU list grows to BUF_LRU_OLD_MIN_LEN length. */ +static +void +buf_LRU_old_init(void) +/*==================*/ +{ + buf_page_t* bpage; + + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&LRU_list_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 + the adjust function to move the LRU_old pointer to the right + position */ + + bpage = UT_LIST_GET_FIRST(buf_pool->LRU); + + while (bpage != NULL) { + ut_ad(bpage->in_LRU_list); + buf_page_set_old(bpage, TRUE); + bpage = UT_LIST_GET_NEXT(LRU, bpage); + } + + buf_pool->LRU_old = UT_LIST_GET_FIRST(buf_pool->LRU); + buf_pool->LRU_old_len = UT_LIST_GET_LEN(buf_pool->LRU); + + buf_LRU_old_adjust_len(); +} + +/********************************************************************** +Remove a block from the unzip_LRU list if it belonged to the list. */ +static +void +buf_unzip_LRU_remove_block_if_needed( +/*=================================*/ + buf_page_t* bpage) /* in/out: control block */ +{ + ut_ad(buf_pool); + ut_ad(bpage); + ut_ad(buf_page_in_file(bpage)); + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&LRU_list_mutex)); + + if (buf_page_belongs_to_unzip_LRU(bpage)) { + buf_block_t* block = (buf_block_t*) bpage; + + ut_ad(block->in_unzip_LRU_list); + block->in_unzip_LRU_list = FALSE; + + UT_LIST_REMOVE(unzip_LRU, buf_pool->unzip_LRU, block); + } +} + +/********************************************************************** +Removes a block from the LRU list. */ +UNIV_INLINE +void +buf_LRU_remove_block( +/*=================*/ + buf_page_t* bpage) /* in: control block */ +{ + ut_ad(buf_pool); + ut_ad(bpage); + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&LRU_list_mutex)); + + ut_a(buf_page_in_file(bpage)); + + ut_ad(bpage->in_LRU_list); + + /* If the LRU_old pointer is defined and points to just this block, + move it backward one step */ + + if (UNIV_UNLIKELY(bpage == buf_pool->LRU_old)) { + + /* Below: the previous block is guaranteed to exist, because + the LRU_old pointer is only allowed to differ by the + tolerance value from strict 3/8 of the LRU list length. */ + + buf_pool->LRU_old = UT_LIST_GET_PREV(LRU, bpage); + ut_a(buf_pool->LRU_old); +#ifdef UNIV_LRU_DEBUG + ut_a(!buf_pool->LRU_old->old); +#endif /* UNIV_LRU_DEBUG */ + buf_page_set_old(buf_pool->LRU_old, TRUE); + + buf_pool->LRU_old_len++; + } + + /* Remove the block from the LRU list */ + UT_LIST_REMOVE(LRU, buf_pool->LRU, bpage); + bpage->in_LRU_list = FALSE; + + buf_unzip_LRU_remove_block_if_needed(bpage); + + /* If the LRU list is so short that LRU_old not defined, return */ + if (UT_LIST_GET_LEN(buf_pool->LRU) < BUF_LRU_OLD_MIN_LEN) { + + buf_pool->LRU_old = NULL; + + return; + } + + ut_ad(buf_pool->LRU_old); + + /* Update the LRU_old_len field if necessary */ + if (buf_page_is_old(bpage)) { + + buf_pool->LRU_old_len--; + } + + /* Adjust the length of the old block list if necessary */ + buf_LRU_old_adjust_len(); +} + +/********************************************************************** +Adds a block to the LRU list of decompressed zip pages. */ +UNIV_INTERN +void +buf_unzip_LRU_add_block( +/*====================*/ + buf_block_t* block, /* in: control block */ + ibool old) /* in: TRUE if should be put to the end + of the list, else put to the start */ +{ + ut_ad(buf_pool); + ut_ad(block); + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&LRU_list_mutex)); + + ut_a(buf_page_belongs_to_unzip_LRU(&block->page)); + + ut_ad(!block->in_unzip_LRU_list); + block->in_unzip_LRU_list = TRUE; + + if (old) { + UT_LIST_ADD_LAST(unzip_LRU, buf_pool->unzip_LRU, block); + } else { + UT_LIST_ADD_FIRST(unzip_LRU, buf_pool->unzip_LRU, block); + } +} + +/********************************************************************** +Adds a block to the LRU list end. */ +UNIV_INLINE +void +buf_LRU_add_block_to_end_low( +/*=========================*/ + buf_page_t* bpage) /* in: control block */ +{ + buf_page_t* last_bpage; + + ut_ad(buf_pool); + ut_ad(bpage); + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&LRU_list_mutex)); + + ut_a(buf_page_in_file(bpage)); + + last_bpage = UT_LIST_GET_LAST(buf_pool->LRU); + + if (last_bpage) { + bpage->LRU_position = last_bpage->LRU_position; + } else { + bpage->LRU_position = buf_pool_clock_tic(); + } + + ut_ad(!bpage->in_LRU_list); + UT_LIST_ADD_LAST(LRU, buf_pool->LRU, bpage); + bpage->in_LRU_list = TRUE; + + buf_page_set_old(bpage, TRUE); + + if (UT_LIST_GET_LEN(buf_pool->LRU) >= BUF_LRU_OLD_MIN_LEN) { + + buf_pool->LRU_old_len++; + } + + if (UT_LIST_GET_LEN(buf_pool->LRU) > BUF_LRU_OLD_MIN_LEN) { + + ut_ad(buf_pool->LRU_old); + + /* Adjust the length of the old block list if necessary */ + + buf_LRU_old_adjust_len(); + + } else if (UT_LIST_GET_LEN(buf_pool->LRU) == BUF_LRU_OLD_MIN_LEN) { + + /* The LRU list is now long enough for LRU_old to become + defined: init it */ + + buf_LRU_old_init(); + } + + /* If this is a zipped block with decompressed frame as well + then put it on the unzip_LRU list */ + if (buf_page_belongs_to_unzip_LRU(bpage)) { + buf_unzip_LRU_add_block((buf_block_t*) bpage, TRUE); + } +} + +/********************************************************************** +Adds a block to the LRU list. */ +UNIV_INLINE +void +buf_LRU_add_block_low( +/*==================*/ + buf_page_t* bpage, /* in: control block */ + ibool old) /* in: TRUE if should be put to the old blocks + in the LRU list, else put to the start; if the + LRU list is very short, the block is added to + the start, regardless of this parameter */ +{ + ut_ad(buf_pool); + ut_ad(bpage); + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&LRU_list_mutex)); + + ut_a(buf_page_in_file(bpage)); + ut_ad(!bpage->in_LRU_list); + + if (!old || (UT_LIST_GET_LEN(buf_pool->LRU) < BUF_LRU_OLD_MIN_LEN)) { + + UT_LIST_ADD_FIRST(LRU, buf_pool->LRU, bpage); + + bpage->LRU_position = buf_pool_clock_tic(); + bpage->freed_page_clock = buf_pool->freed_page_clock; + } else { +#ifdef UNIV_LRU_DEBUG + /* buf_pool->LRU_old must be the first item in the LRU list + whose "old" flag is set. */ + ut_a(buf_pool->LRU_old->old); + ut_a(!UT_LIST_GET_PREV(LRU, buf_pool->LRU_old) + || !UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)->old); + ut_a(!UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old) + || UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)->old); +#endif /* UNIV_LRU_DEBUG */ + UT_LIST_INSERT_AFTER(LRU, buf_pool->LRU, buf_pool->LRU_old, + bpage); + buf_pool->LRU_old_len++; + + /* We copy the LRU position field of the previous block + to the new block */ + + bpage->LRU_position = (buf_pool->LRU_old)->LRU_position; + } + + bpage->in_LRU_list = TRUE; + + buf_page_set_old(bpage, old); + + if (UT_LIST_GET_LEN(buf_pool->LRU) > BUF_LRU_OLD_MIN_LEN) { + + ut_ad(buf_pool->LRU_old); + + /* Adjust the length of the old block list if necessary */ + + buf_LRU_old_adjust_len(); + + } else if (UT_LIST_GET_LEN(buf_pool->LRU) == BUF_LRU_OLD_MIN_LEN) { + + /* The LRU list is now long enough for LRU_old to become + defined: init it */ + + buf_LRU_old_init(); + } + + /* If this is a zipped block with decompressed frame as well + then put it on the unzip_LRU list */ + if (buf_page_belongs_to_unzip_LRU(bpage)) { + buf_unzip_LRU_add_block((buf_block_t*) bpage, old); + } +} + +/********************************************************************** +Adds a block to the LRU list. */ +UNIV_INTERN +void +buf_LRU_add_block( +/*==============*/ + buf_page_t* bpage, /* in: control block */ + ibool old) /* in: TRUE if should be put to the old + blocks in the LRU list, else put to the start; + if the LRU list is very short, the block is + added to the start, regardless of this + parameter */ +{ + buf_LRU_add_block_low(bpage, old); +} + +/********************************************************************** +Moves a block to the start of the LRU list. */ +UNIV_INTERN +void +buf_LRU_make_block_young( +/*=====================*/ + buf_page_t* bpage) /* in: control block */ +{ + buf_LRU_remove_block(bpage); + buf_LRU_add_block_low(bpage, FALSE); +} + +/********************************************************************** +Moves a block to the end of the LRU list. */ +UNIV_INTERN +void +buf_LRU_make_block_old( +/*===================*/ + buf_page_t* bpage) /* in: control block */ +{ + buf_LRU_remove_block(bpage); + buf_LRU_add_block_to_end_low(bpage); +} + +/********************************************************************** +Try to free a block. If bpage is a descriptor of a compressed-only +page, the descriptor object will be freed as well. + +NOTE: If this function returns BUF_LRU_FREED, it will not temporarily +release buf_pool_mutex. Furthermore, the page frame will no longer be +accessible via bpage. + +The caller must hold buf_pool_mutex and buf_page_get_mutex(bpage) and +release these two mutexes after the call. No other +buf_page_get_mutex() may be held when calling this function. */ +UNIV_INTERN +enum buf_lru_free_block_status +buf_LRU_free_block( +/*===============*/ + /* out: BUF_LRU_FREED if freed, + BUF_LRU_CANNOT_RELOCATE or + BUF_LRU_NOT_FREED otherwise. */ + buf_page_t* bpage, /* in: block to be freed */ + ibool zip, /* in: TRUE if should remove also the + compressed page of an uncompressed page */ + ibool* buf_pool_mutex_released, + /* in: pointer to a variable that will + be assigned TRUE if buf_pool_mutex + was temporarily released, or NULL */ + ibool have_LRU_mutex) +{ + buf_page_t* b = NULL; + mutex_t* block_mutex = buf_page_get_mutex(bpage); + + //ut_ad(buf_pool_mutex_own()); + /* optimistic */ + //ut_ad(mutex_own(block_mutex)); + //ut_ad(buf_page_in_file(bpage)); + //ut_ad(bpage->in_LRU_list); + //ut_ad(!bpage->in_flush_list == !bpage->oldest_modification); + UNIV_MEM_ASSERT_RW(bpage, sizeof *bpage); + + if (!bpage->in_LRU_list || !block_mutex || !buf_page_can_relocate(bpage)) { + + /* Do not free buffer-fixed or I/O-fixed blocks. */ + return(BUF_LRU_NOT_FREED); + } + +#ifdef UNIV_IBUF_COUNT_DEBUG + ut_a(ibuf_count_get(bpage->space, bpage->offset) == 0); +#endif /* UNIV_IBUF_COUNT_DEBUG */ + + if (zip || !bpage->zip.data) { + /* This would completely free the block. */ + /* Do not completely free dirty blocks. */ + + if (bpage->oldest_modification) { + return(BUF_LRU_NOT_FREED); + } + } else if (bpage->oldest_modification) { + /* Do not completely free dirty blocks. */ + + if (buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE) { + ut_ad(buf_page_get_state(bpage) + == BUF_BLOCK_ZIP_DIRTY); + return(BUF_LRU_NOT_FREED); + } + + goto alloc; + } else if (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE) { + /* Allocate the control block for the compressed page. + If it cannot be allocated (without freeing a block + from the LRU list), refuse to free bpage. */ +alloc: + //buf_pool_mutex_exit_forbid(); + b = buf_buddy_alloc(sizeof *b, NULL, FALSE); + //buf_pool_mutex_exit_allow(); + + if (UNIV_UNLIKELY(!b)) { + return(BUF_LRU_CANNOT_RELOCATE); + } + + //memcpy(b, bpage, sizeof *b); + } + +#ifdef UNIV_DEBUG + if (buf_debug_prints) { + fprintf(stderr, "Putting space %lu page %lu to free list\n", + (ulong) buf_page_get_space(bpage), + (ulong) buf_page_get_page_no(bpage)); + } +#endif /* UNIV_DEBUG */ + + /* not to break latch order, must re-enter block_mutex */ + mutex_exit(block_mutex); + + if (!have_LRU_mutex) + mutex_enter(&LRU_list_mutex); /* optimistic */ + mutex_enter(&flush_list_mutex); + rw_lock_x_lock(&page_hash_latch); + mutex_enter(block_mutex); + + /* recheck states of block */ + if (!bpage->in_LRU_list || block_mutex != buf_page_get_mutex(bpage) + || !buf_page_can_relocate(bpage)) { +not_freed: + if (b) { + buf_buddy_free(b, sizeof *b, TRUE); + } + if (!have_LRU_mutex) + mutex_exit(&LRU_list_mutex); + mutex_exit(&flush_list_mutex); + rw_lock_x_unlock(&page_hash_latch); + return(BUF_LRU_NOT_FREED); + } else if (zip || !bpage->zip.data) { + if (bpage->oldest_modification) + goto not_freed; + } else if (bpage->oldest_modification) { + if (buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE) { + ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_DIRTY); + goto not_freed; + } + } + + if (b) { + memcpy(b, bpage, sizeof *b); + } + + if (buf_LRU_block_remove_hashed_page(bpage, zip) + != BUF_BLOCK_ZIP_FREE) { + ut_a(bpage->buf_fix_count == 0); + + if (b) { + buf_page_t* prev_b = UT_LIST_GET_PREV(LRU, b); + const ulint fold = buf_page_address_fold( + bpage->space, bpage->offset); + + ut_a(!buf_page_hash_get(bpage->space, bpage->offset)); + + while (prev_b && !prev_b->in_LRU_list) { + prev_b = UT_LIST_GET_PREV(LRU, prev_b); + } + + b->state = b->oldest_modification + ? BUF_BLOCK_ZIP_DIRTY + : BUF_BLOCK_ZIP_PAGE; + UNIV_MEM_DESC(b->zip.data, + page_zip_get_size(&b->zip), b); + + /* The fields in_page_hash and in_LRU_list of + the to-be-freed block descriptor should have + been cleared in + buf_LRU_block_remove_hashed_page(), which + invokes buf_LRU_remove_block(). */ + ut_ad(!bpage->in_page_hash); + ut_ad(!bpage->in_LRU_list); + /* bpage->state was BUF_BLOCK_FILE_PAGE because + b != NULL. The type cast below is thus valid. */ + ut_ad(!((buf_block_t*) bpage)->in_unzip_LRU_list); + + /* The fields of bpage were copied to b before + buf_LRU_block_remove_hashed_page() was invoked. */ + ut_ad(!b->in_zip_hash); + ut_ad(b->in_page_hash); + ut_ad(b->in_LRU_list); + + HASH_INSERT(buf_page_t, hash, + buf_pool->page_hash, fold, b); + + /* Insert b where bpage was in the LRU list. */ + if (UNIV_LIKELY(prev_b != NULL)) { + ulint lru_len; + + ut_ad(prev_b->in_LRU_list); + ut_ad(buf_page_in_file(prev_b)); + UNIV_MEM_ASSERT_RW(prev_b, sizeof *prev_b); + + UT_LIST_INSERT_AFTER(LRU, buf_pool->LRU, + prev_b, b); + + if (buf_page_is_old(b)) { + buf_pool->LRU_old_len++; + if (UNIV_UNLIKELY + (buf_pool->LRU_old + == UT_LIST_GET_NEXT(LRU, b))) { + + buf_pool->LRU_old = b; + } +#ifdef UNIV_LRU_DEBUG + ut_a(prev_b->old + || !UT_LIST_GET_NEXT(LRU, b) + || UT_LIST_GET_NEXT(LRU, b)->old); + } else { + ut_a(!prev_b->old + || !UT_LIST_GET_NEXT(LRU, b) + || !UT_LIST_GET_NEXT(LRU, b)->old); +#endif /* UNIV_LRU_DEBUG */ + } + + lru_len = UT_LIST_GET_LEN(buf_pool->LRU); + + if (lru_len > BUF_LRU_OLD_MIN_LEN) { + ut_ad(buf_pool->LRU_old); + /* Adjust the length of the + old block list if necessary */ + buf_LRU_old_adjust_len(); + } else if (lru_len == BUF_LRU_OLD_MIN_LEN) { + /* The LRU list is now long + enough for LRU_old to become + defined: init it */ + buf_LRU_old_init(); + } + } else { + b->in_LRU_list = FALSE; + buf_LRU_add_block_low(b, buf_page_is_old(b)); + } + + if (b->state == BUF_BLOCK_ZIP_PAGE) { + buf_LRU_insert_zip_clean(b); + } else { + buf_page_t* prev; + + ut_ad(b->in_flush_list); + ut_d(bpage->in_flush_list = FALSE); + + prev = UT_LIST_GET_PREV(list, b); + UT_LIST_REMOVE(list, buf_pool->flush_list, b); + + if (prev) { + ut_ad(prev->in_flush_list); + UT_LIST_INSERT_AFTER( + list, + buf_pool->flush_list, + prev, b); + } else { + UT_LIST_ADD_FIRST( + list, + buf_pool->flush_list, + b); + } + } + + bpage->zip.data = NULL; + page_zip_set_size(&bpage->zip, 0); + + /* Prevent buf_page_get_gen() from + decompressing the block while we release + buf_pool_mutex and block_mutex. */ + b->buf_fix_count++; + b->io_fix = BUF_IO_READ; + } + + if (buf_pool_mutex_released) { + *buf_pool_mutex_released = TRUE; + } + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + mutex_exit(&flush_list_mutex); + rw_lock_x_unlock(&page_hash_latch); + mutex_exit(block_mutex); + + /* Remove possible adaptive hash index on the page. + The page was declared uninitialized by + buf_LRU_block_remove_hashed_page(). We need to flag + the contents of the page valid (which it still is) in + order to avoid bogus Valgrind warnings.*/ + + UNIV_MEM_VALID(((buf_block_t*) bpage)->frame, + UNIV_PAGE_SIZE); + btr_search_drop_page_hash_index((buf_block_t*) bpage); + UNIV_MEM_INVALID(((buf_block_t*) bpage)->frame, + UNIV_PAGE_SIZE); + + if (b) { + /* Compute and stamp the compressed page + checksum while not holding any mutex. The + block is already half-freed + (BUF_BLOCK_REMOVE_HASH) and removed from + buf_pool->page_hash, thus inaccessible by any + other thread. */ + + mach_write_to_4( + b->zip.data + FIL_PAGE_SPACE_OR_CHKSUM, + UNIV_LIKELY(srv_use_checksums) + ? page_zip_calc_checksum( + b->zip.data, + page_zip_get_size(&b->zip)) + : BUF_NO_CHECKSUM_MAGIC); + } + + //buf_pool_mutex_enter(); + if (have_LRU_mutex) + mutex_enter(&LRU_list_mutex); + mutex_enter(block_mutex); + + if (b) { + mutex_enter(&buf_pool_zip_mutex); + b->buf_fix_count--; + buf_page_set_io_fix(b, BUF_IO_NONE); + mutex_exit(&buf_pool_zip_mutex); + } + + buf_LRU_block_free_hashed_page((buf_block_t*) bpage, FALSE); + } else { + /* The block_mutex should have been released by + buf_LRU_block_remove_hashed_page() when it returns + BUF_BLOCK_ZIP_FREE. */ + ut_ad(block_mutex == &buf_pool_zip_mutex); + mutex_enter(block_mutex); + + if (!have_LRU_mutex) + mutex_exit(&LRU_list_mutex); + mutex_exit(&flush_list_mutex); + rw_lock_x_unlock(&page_hash_latch); + } + + return(BUF_LRU_FREED); +} + +/********************************************************************** +Puts a block back to the free list. */ +UNIV_INTERN +void +buf_LRU_block_free_non_file_page( +/*=============================*/ + buf_block_t* block, /* in: block, must not contain a file page */ + ibool have_page_hash_mutex) +{ + void* data; + + ut_ad(block); + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&block->mutex)); + + switch (buf_block_get_state(block)) { + case BUF_BLOCK_MEMORY: + case BUF_BLOCK_READY_FOR_USE: + break; + default: + ut_error; + } + +#if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG + ut_a(block->n_pointers == 0); +#endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ + ut_ad(!block->page.in_free_list); + ut_ad(!block->page.in_flush_list); + ut_ad(!block->page.in_LRU_list); + + buf_block_set_state(block, BUF_BLOCK_NOT_USED); + + UNIV_MEM_ALLOC(block->frame, UNIV_PAGE_SIZE); +#ifdef UNIV_DEBUG + /* Wipe contents of page to reveal possible stale pointers to it */ + memset(block->frame, '\0', UNIV_PAGE_SIZE); +#else + /* Wipe page_no and space_id */ + memset(block->frame + FIL_PAGE_OFFSET, 0xfe, 4); + memset(block->frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 0xfe, 4); +#endif + data = block->page.zip.data; + + if (data) { + block->page.zip.data = NULL; + mutex_exit(&block->mutex); + //buf_pool_mutex_exit_forbid(); + buf_buddy_free(data, page_zip_get_size(&block->page.zip), have_page_hash_mutex); + //buf_pool_mutex_exit_allow(); + mutex_enter(&block->mutex); + page_zip_set_size(&block->page.zip, 0); + } + + mutex_enter(&free_list_mutex); + UT_LIST_ADD_FIRST(list, buf_pool->free, (&block->page)); + ut_d(block->page.in_free_list = TRUE); + mutex_exit(&free_list_mutex); + + UNIV_MEM_ASSERT_AND_FREE(block->frame, UNIV_PAGE_SIZE); +} + +/********************************************************************** +Takes a block out of the LRU list and page hash table. +If the block is compressed-only (BUF_BLOCK_ZIP_PAGE), +the object will be freed and buf_pool_zip_mutex will be released. + +If a compressed page or a compressed-only block descriptor is freed, +other compressed pages or compressed-only block descriptors may be +relocated. */ +static +enum buf_page_state +buf_LRU_block_remove_hashed_page( +/*=============================*/ + /* out: the new state of the block + (BUF_BLOCK_ZIP_FREE if the state was + BUF_BLOCK_ZIP_PAGE, or BUF_BLOCK_REMOVE_HASH + otherwise) */ + buf_page_t* bpage, /* in: block, must contain a file page and + be in a state where it can be freed; there + may or may not be a hash index to the page */ + ibool zip) /* in: TRUE if should remove also the + compressed page of an uncompressed page */ +{ + const buf_page_t* hashed_bpage; + ut_ad(bpage); + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&LRU_list_mutex)); +#ifdef UNIV_SYNC_DEBUG + ut_ad(rw_lock_own(&page_hash_latch, RW_LOCK_EX)); +#endif + ut_ad(mutex_own(buf_page_get_mutex(bpage))); + + ut_a(buf_page_get_io_fix(bpage) == BUF_IO_NONE); + ut_a(bpage->buf_fix_count == 0); + + UNIV_MEM_ASSERT_RW(bpage, sizeof *bpage); + + buf_LRU_remove_block(bpage); + + buf_pool->freed_page_clock += 1; + + switch (buf_page_get_state(bpage)) { + case BUF_BLOCK_FILE_PAGE: + UNIV_MEM_ASSERT_W(bpage, sizeof(buf_block_t)); + UNIV_MEM_ASSERT_W(((buf_block_t*) bpage)->frame, + UNIV_PAGE_SIZE); + buf_block_modify_clock_inc((buf_block_t*) bpage); + if (bpage->zip.data) { + const page_t* page = ((buf_block_t*) bpage)->frame; + const ulint zip_size + = page_zip_get_size(&bpage->zip); + + ut_a(!zip || bpage->oldest_modification == 0); + + switch (UNIV_EXPECT(fil_page_get_type(page), + FIL_PAGE_INDEX)) { + case FIL_PAGE_TYPE_ALLOCATED: + case FIL_PAGE_INODE: + case FIL_PAGE_IBUF_BITMAP: + case FIL_PAGE_TYPE_FSP_HDR: + case FIL_PAGE_TYPE_XDES: + /* These are essentially uncompressed pages. */ + if (!zip) { + /* InnoDB writes the data to the + uncompressed page frame. Copy it + to the compressed page, which will + be preserved. */ + memcpy(bpage->zip.data, page, + zip_size); + } + break; + case FIL_PAGE_TYPE_ZBLOB: + case FIL_PAGE_TYPE_ZBLOB2: + break; + case FIL_PAGE_INDEX: +#ifdef UNIV_ZIP_DEBUG + ut_a(page_zip_validate(&bpage->zip, page)); +#endif /* UNIV_ZIP_DEBUG */ + break; + default: + ut_print_timestamp(stderr); + fputs(" InnoDB: ERROR: The compressed page" + " to be evicted seems corrupt:", stderr); + ut_print_buf(stderr, page, zip_size); + fputs("\nInnoDB: Possibly older version" + " of the page:", stderr); + ut_print_buf(stderr, bpage->zip.data, + zip_size); + putc('\n', stderr); + ut_error; + } + + break; + } + /* fall through */ + case BUF_BLOCK_ZIP_PAGE: + ut_a(bpage->oldest_modification == 0); + UNIV_MEM_ASSERT_W(bpage->zip.data, + page_zip_get_size(&bpage->zip)); + break; + case BUF_BLOCK_ZIP_FREE: + case BUF_BLOCK_ZIP_DIRTY: + case BUF_BLOCK_NOT_USED: + case BUF_BLOCK_READY_FOR_USE: + case BUF_BLOCK_MEMORY: + case BUF_BLOCK_REMOVE_HASH: + ut_error; + break; + } + + hashed_bpage = buf_page_hash_get(bpage->space, bpage->offset); + + if (UNIV_UNLIKELY(bpage != hashed_bpage)) { + fprintf(stderr, + "InnoDB: Error: page %lu %lu not found" + " in the hash table\n", + (ulong) bpage->space, + (ulong) bpage->offset); + if (hashed_bpage) { + fprintf(stderr, + "InnoDB: In hash table we find block" + " %p of %lu %lu which is not %p\n", + (const void*) hashed_bpage, + (ulong) hashed_bpage->space, + (ulong) hashed_bpage->offset, + (const void*) bpage); + } + +#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG + mutex_exit(buf_page_get_mutex(bpage)); + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + rw_lock_x_unlock(&page_hash_latch); + buf_print(); + buf_LRU_print(); + buf_validate(); + buf_LRU_validate(); +#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ + ut_error; + } + + ut_ad(!bpage->in_zip_hash); + ut_ad(bpage->in_page_hash); + ut_d(bpage->in_page_hash = FALSE); + HASH_DELETE(buf_page_t, hash, buf_pool->page_hash, + buf_page_address_fold(bpage->space, bpage->offset), + bpage); + switch (buf_page_get_state(bpage)) { + case BUF_BLOCK_ZIP_PAGE: + ut_ad(!bpage->in_free_list); + ut_ad(!bpage->in_flush_list); + ut_ad(!bpage->in_LRU_list); + ut_a(bpage->zip.data); + ut_a(buf_page_get_zip_size(bpage)); + + UT_LIST_REMOVE(list, buf_pool->zip_clean, bpage); + + mutex_exit(&buf_pool_zip_mutex); + //buf_pool_mutex_exit_forbid(); + buf_buddy_free(bpage->zip.data, + page_zip_get_size(&bpage->zip), TRUE); + buf_buddy_free(bpage, sizeof(*bpage), TRUE); + //buf_pool_mutex_exit_allow(); + UNIV_MEM_UNDESC(bpage); + return(BUF_BLOCK_ZIP_FREE); + + case BUF_BLOCK_FILE_PAGE: + memset(((buf_block_t*) bpage)->frame + + FIL_PAGE_OFFSET, 0xff, 4); + memset(((buf_block_t*) bpage)->frame + + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 0xff, 4); + UNIV_MEM_INVALID(((buf_block_t*) bpage)->frame, + UNIV_PAGE_SIZE); + buf_page_set_state(bpage, BUF_BLOCK_REMOVE_HASH); + + if (zip && bpage->zip.data) { + /* Free the compressed page. */ + void* data = bpage->zip.data; + bpage->zip.data = NULL; + + mutex_exit(&((buf_block_t*) bpage)->mutex); + //buf_pool_mutex_exit_forbid(); + buf_buddy_free(data, page_zip_get_size(&bpage->zip), TRUE); + //buf_pool_mutex_exit_allow(); + mutex_enter(&((buf_block_t*) bpage)->mutex); + page_zip_set_size(&bpage->zip, 0); + } + + return(BUF_BLOCK_REMOVE_HASH); + + case BUF_BLOCK_ZIP_FREE: + case BUF_BLOCK_ZIP_DIRTY: + case BUF_BLOCK_NOT_USED: + case BUF_BLOCK_READY_FOR_USE: + case BUF_BLOCK_MEMORY: + case BUF_BLOCK_REMOVE_HASH: + break; + } + + ut_error; + return(BUF_BLOCK_ZIP_FREE); +} + +/********************************************************************** +Puts a file page whose has no hash index to the free list. */ +static +void +buf_LRU_block_free_hashed_page( +/*===========================*/ + buf_block_t* block, /* in: block, must contain a file page and + be in a state where it can be freed */ + ibool have_page_hash_mutex) +{ + //ut_ad(buf_pool_mutex_own()); + ut_ad(mutex_own(&block->mutex)); + + buf_block_set_state(block, BUF_BLOCK_MEMORY); + + buf_LRU_block_free_non_file_page(block, have_page_hash_mutex); +} + +/************************************************************************ +Update the historical stats that we are collecting for LRU eviction +policy at the end of each interval. */ +UNIV_INTERN +void +buf_LRU_stat_update(void) +/*=====================*/ +{ + buf_LRU_stat_t* item; + + /* If we haven't started eviction yet then don't update stats. */ + if (buf_pool->freed_page_clock == 0) { + goto func_exit; + } + + //buf_pool_mutex_enter(); + mutex_enter(&buf_pool_mutex); + + /* Update the index. */ + item = &buf_LRU_stat_arr[buf_LRU_stat_arr_ind]; + buf_LRU_stat_arr_ind++; + buf_LRU_stat_arr_ind %= BUF_LRU_STAT_N_INTERVAL; + + /* Add the current value and subtract the obsolete entry. */ + buf_LRU_stat_sum.io += buf_LRU_stat_cur.io - item->io; + buf_LRU_stat_sum.unzip += buf_LRU_stat_cur.unzip - item->unzip; + + /* Put current entry in the array. */ + memcpy(item, &buf_LRU_stat_cur, sizeof *item); + + //buf_pool_mutex_exit(); + mutex_exit(&buf_pool_mutex); + +func_exit: + /* Clear the current entry. */ + memset(&buf_LRU_stat_cur, 0, sizeof buf_LRU_stat_cur); +} + +#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG +/************************************************************************** +Validates the LRU list. */ +UNIV_INTERN +ibool +buf_LRU_validate(void) +/*==================*/ +{ + buf_page_t* bpage; + buf_block_t* block; + ulint old_len; + ulint new_len; + ulint LRU_pos; + + ut_ad(buf_pool); + //buf_pool_mutex_enter(); + mutex_enter(&LRU_list_mutex); + + if (UT_LIST_GET_LEN(buf_pool->LRU) >= BUF_LRU_OLD_MIN_LEN) { + + ut_a(buf_pool->LRU_old); + old_len = buf_pool->LRU_old_len; + new_len = 3 * (UT_LIST_GET_LEN(buf_pool->LRU) / 8); + ut_a(old_len >= new_len - BUF_LRU_OLD_TOLERANCE); + ut_a(old_len <= new_len + BUF_LRU_OLD_TOLERANCE); + } + + UT_LIST_VALIDATE(LRU, buf_page_t, buf_pool->LRU); + + bpage = UT_LIST_GET_FIRST(buf_pool->LRU); + + old_len = 0; + + while (bpage != NULL) { + + switch (buf_page_get_state(bpage)) { + case BUF_BLOCK_ZIP_FREE: + case BUF_BLOCK_NOT_USED: + case BUF_BLOCK_READY_FOR_USE: + case BUF_BLOCK_MEMORY: + case BUF_BLOCK_REMOVE_HASH: + ut_error; + break; + case BUF_BLOCK_FILE_PAGE: + ut_ad(((buf_block_t*) bpage)->in_unzip_LRU_list + == buf_page_belongs_to_unzip_LRU(bpage)); + case BUF_BLOCK_ZIP_PAGE: + case BUF_BLOCK_ZIP_DIRTY: + break; + } + + if (buf_page_is_old(bpage)) { + old_len++; + } + + if (buf_pool->LRU_old && (old_len == 1)) { + ut_a(buf_pool->LRU_old == bpage); + } + + LRU_pos = buf_page_get_LRU_position(bpage); + + bpage = UT_LIST_GET_NEXT(LRU, bpage); + + if (bpage) { + /* If the following assert fails, it may + not be an error: just the buf_pool clock + has wrapped around */ + ut_a(LRU_pos >= buf_page_get_LRU_position(bpage)); + } + } + + if (buf_pool->LRU_old) { + ut_a(buf_pool->LRU_old_len == old_len); + } + + mutex_exit(&LRU_list_mutex); + mutex_enter(&free_list_mutex); + + UT_LIST_VALIDATE(list, buf_page_t, buf_pool->free); + + for (bpage = UT_LIST_GET_FIRST(buf_pool->free); + bpage != NULL; + bpage = UT_LIST_GET_NEXT(list, bpage)) { + + ut_a(buf_page_get_state(bpage) == BUF_BLOCK_NOT_USED); + } + + mutex_exit(&free_list_mutex); + mutex_enter(&LRU_list_mutex); + + UT_LIST_VALIDATE(unzip_LRU, buf_block_t, buf_pool->unzip_LRU); + + for (block = UT_LIST_GET_FIRST(buf_pool->unzip_LRU); + block; + block = UT_LIST_GET_NEXT(unzip_LRU, block)) { + + ut_ad(block->in_unzip_LRU_list); + ut_ad(block->page.in_LRU_list); + ut_a(buf_page_belongs_to_unzip_LRU(&block->page)); + } + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); + return(TRUE); +} +#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ + +#if defined UNIV_DEBUG_PRINT || defined UNIV_DEBUG || defined UNIV_BUF_DEBUG +/************************************************************************** +Prints the LRU list. */ +UNIV_INTERN +void +buf_LRU_print(void) +/*===============*/ +{ + const buf_page_t* bpage; + + ut_ad(buf_pool); + //buf_pool_mutex_enter(); + mutex_enter(&LRU_list_mutex); + + fprintf(stderr, "Pool ulint clock %lu\n", + (ulong) buf_pool->ulint_clock); + + bpage = UT_LIST_GET_FIRST(buf_pool->LRU); + + while (bpage != NULL) { + + fprintf(stderr, "BLOCK space %lu page %lu ", + (ulong) buf_page_get_space(bpage), + (ulong) buf_page_get_page_no(bpage)); + + if (buf_page_is_old(bpage)) { + fputs("old ", stderr); + } + + if (bpage->buf_fix_count) { + fprintf(stderr, "buffix count %lu ", + (ulong) bpage->buf_fix_count); + } + + if (buf_page_get_io_fix(bpage)) { + fprintf(stderr, "io_fix %lu ", + (ulong) buf_page_get_io_fix(bpage)); + } + + if (bpage->oldest_modification) { + fputs("modif. ", stderr); + } + + switch (buf_page_get_state(bpage)) { + const byte* frame; + case BUF_BLOCK_FILE_PAGE: + frame = buf_block_get_frame((buf_block_t*) bpage); + fprintf(stderr, "\nLRU pos %lu type %lu" + " index id %lu\n", + (ulong) buf_page_get_LRU_position(bpage), + (ulong) fil_page_get_type(frame), + (ulong) ut_dulint_get_low( + btr_page_get_index_id(frame))); + break; + case BUF_BLOCK_ZIP_PAGE: + frame = bpage->zip.data; + fprintf(stderr, "\nLRU pos %lu type %lu size %lu" + " index id %lu\n", + (ulong) buf_page_get_LRU_position(bpage), + (ulong) fil_page_get_type(frame), + (ulong) buf_page_get_zip_size(bpage), + (ulong) ut_dulint_get_low( + btr_page_get_index_id(frame))); + break; + + default: + fprintf(stderr, "\nLRU pos %lu !state %lu!\n", + (ulong) buf_page_get_LRU_position(bpage), + (ulong) buf_page_get_state(bpage)); + break; + } + + bpage = UT_LIST_GET_NEXT(LRU, bpage); + } + + //buf_pool_mutex_exit(); + mutex_exit(&LRU_list_mutex); +} +#endif /* UNIV_DEBUG_PRINT || UNIV_DEBUG || UNIV_BUF_DEBUG */ |