diff options
Diffstat (limited to 'storage/innobase/buf/buf0buddy.cc')
-rw-r--r-- | storage/innobase/buf/buf0buddy.cc | 253 |
1 files changed, 206 insertions, 47 deletions
diff --git a/storage/innobase/buf/buf0buddy.cc b/storage/innobase/buf/buf0buddy.cc index f2ab73217e0..1d6083a5f77 100644 --- a/storage/innobase/buf/buf0buddy.cc +++ b/storage/innobase/buf/buf0buddy.cc @@ -23,12 +23,10 @@ Binary buddy allocator for compressed pages Created December 2006 by Marko Makela *******************************************************/ -#define THIS_MODULE #include "buf0buddy.h" #ifdef UNIV_NONINL # include "buf0buddy.ic" #endif -#undef THIS_MODULE #include "buf0buf.h" #include "buf0lru.h" #include "buf0flu.h" @@ -71,11 +69,11 @@ are written.*/ /** Value that we stamp on all buffers that are currently on the zip_free list. This value is stamped at BUF_BUDDY_STAMP_OFFSET offset */ -#define BUF_BUDDY_STAMP_FREE (SRV_LOG_SPACE_FIRST_ID) +#define BUF_BUDDY_STAMP_FREE SRV_LOG_SPACE_FIRST_ID /** Stamp value for non-free buffers. Will be overwritten by a non-zero value by the consumer of the block */ -#define BUF_BUDDY_STAMP_NONFREE (0XFFFFFFFF) +#define BUF_BUDDY_STAMP_NONFREE 0XFFFFFFFFUL #if BUF_BUDDY_STAMP_FREE >= BUF_BUDDY_STAMP_NONFREE # error "BUF_BUDDY_STAMP_FREE >= BUF_BUDDY_STAMP_NONFREE" @@ -111,7 +109,7 @@ buf_buddy_mem_invalid( /**********************************************************************//** Check if a buddy is stamped free. -@return whether the buddy is free */ +@return whether the buddy is free */ UNIV_INLINE MY_ATTRIBUTE((warn_unused_result)) bool buf_buddy_stamp_is_free( @@ -140,7 +138,7 @@ buf_buddy_stamp_free( /**********************************************************************//** Stamps a buddy nonfree. -@param[in/out] buf block to stamp +@param[in,out] buf block to stamp @param[in] i block size */ #define buf_buddy_stamp_nonfree(buf, i) do { \ buf_buddy_mem_invalid(buf, i); \ @@ -152,7 +150,7 @@ Stamps a buddy nonfree. /**********************************************************************//** Get the offset of the buddy of a compressed page frame. -@return the buddy relative of page */ +@return the buddy relative of page */ UNIV_INLINE void* buf_buddy_get( @@ -174,23 +172,33 @@ buf_buddy_get( } } +#ifdef UNIV_DEBUG /** Validate a given zip_free list. */ struct CheckZipFree { - ulint i; - CheckZipFree(ulint i) : i (i) {} + CheckZipFree(ulint i) : m_i(i) {} void operator()(const buf_buddy_free_t* elem) const { ut_a(buf_buddy_stamp_is_free(elem)); - ut_a(elem->stamp.size <= i); + ut_a(elem->stamp.size <= m_i); } + + ulint m_i; }; -#define BUF_BUDDY_LIST_VALIDATE(bp, i) \ - UT_LIST_VALIDATE(list, buf_buddy_free_t, \ - bp->zip_free[i], CheckZipFree(i)) +/** Validate a buddy list. +@param[in] buf_pool buffer pool instance +@param[in] i buddy size to validate */ +static +void +buf_buddy_list_validate( + const buf_pool_t* buf_pool, + ulint i) +{ + CheckZipFree check(i); + ut_list_validate(buf_pool->zip_free[i], check); +} -#ifdef UNIV_DEBUG /**********************************************************************//** Debug function to validate that a buffer is indeed free i.e.: in the zip_free[]. @@ -282,8 +290,8 @@ buf_buddy_add_to_free( ut_ad(buf_pool->zip_free[i].start != buf); buf_buddy_stamp_free(buf, i); - UT_LIST_ADD_FIRST(list, buf_pool->zip_free[i], buf); - ut_d(BUF_BUDDY_LIST_VALIDATE(buf_pool, i)); + UT_LIST_ADD_FIRST(buf_pool->zip_free[i], buf); + ut_d(buf_buddy_list_validate(buf_pool, i)); } /**********************************************************************//** @@ -293,20 +301,21 @@ void buf_buddy_remove_from_free( /*=======================*/ buf_pool_t* buf_pool, /*!< in: buffer pool instance */ - buf_buddy_free_t* buf, /*!< in,own: block to be freed */ + buf_buddy_free_t* buf, /*!< in,own: block to be + freed */ ulint i) /*!< in: index of buf_pool->zip_free[] */ { ut_ad(buf_pool_mutex_own(buf_pool)); ut_ad(buf_buddy_check_free(buf_pool, buf, i)); - UT_LIST_REMOVE(list, buf_pool->zip_free[i], buf); + UT_LIST_REMOVE(buf_pool->zip_free[i], buf); buf_buddy_stamp_nonfree(buf, i); } /**********************************************************************//** Try to allocate a block from buf_pool->zip_free[]. -@return allocated block, or NULL if buf_pool->zip_free[] was empty */ +@return allocated block, or NULL if buf_pool->zip_free[] was empty */ static buf_buddy_free_t* buf_buddy_alloc_zip( @@ -320,10 +329,22 @@ buf_buddy_alloc_zip( ut_a(i < BUF_BUDDY_SIZES); ut_a(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN)); - ut_d(BUF_BUDDY_LIST_VALIDATE(buf_pool, i)); + ut_d(buf_buddy_list_validate(buf_pool, i)); buf = UT_LIST_GET_FIRST(buf_pool->zip_free[i]); + if (buf_pool->curr_size < buf_pool->old_size + && UT_LIST_GET_LEN(buf_pool->withdraw) + < buf_pool->withdraw_target) { + + while (buf != NULL + && buf_frame_will_withdrawn( + buf_pool, reinterpret_cast<byte*>(buf))) { + /* This should be withdrawn, not to be allocated */ + buf = UT_LIST_GET_NEXT(list, buf); + } + } + if (buf) { buf_buddy_remove_from_free(buf_pool, buf, i); } else if (i + 1 < BUF_BUDDY_SIZES) { @@ -388,9 +409,9 @@ buf_buddy_block_free( UNIV_MEM_INVALID(buf, UNIV_PAGE_SIZE); block = (buf_block_t*) bpage; - mutex_enter(&block->mutex); + buf_page_mutex_enter(block); buf_LRU_block_free_non_file_page(block); - mutex_exit(&block->mutex); + buf_page_mutex_exit(block); ut_ad(buf_pool->buddy_n_frames > 0); ut_d(buf_pool->buddy_n_frames--); @@ -425,7 +446,7 @@ buf_buddy_block_register( /**********************************************************************//** Allocate a block from a bigger object. -@return allocated block */ +@return allocated block */ static void* buf_buddy_alloc_from( @@ -463,8 +484,7 @@ buf_buddy_alloc_from( Allocate a block. The thread calling this function must hold buf_pool->mutex and must not hold buf_pool->zip_mutex or any block->mutex. The buf_pool_mutex may be released and reacquired. -@return allocated block, never NULL */ -UNIV_INTERN +@return allocated block, never NULL */ void* buf_buddy_alloc_low( /*================*/ @@ -520,7 +540,7 @@ func_exit: /**********************************************************************//** Try to relocate a block. -@return true if relocated */ +@return true if relocated */ static bool buf_buddy_relocate( @@ -528,11 +548,13 @@ buf_buddy_relocate( buf_pool_t* buf_pool, /*!< in: buffer pool instance */ void* src, /*!< in: block to relocate */ void* dst, /*!< in: free block to relocate to */ - ulint i) /*!< in: index of + ulint i, /*!< in: index of buf_pool->zip_free[] */ + bool force) /*!< in: true if we must relocate + always */ { buf_page_t* bpage; - const ulint size = BUF_BUDDY_LOW << i; + const ulint size = BUF_BUDDY_LOW << i; ulint space; ulint offset; @@ -555,12 +577,19 @@ buf_buddy_relocate( ut_ad(space != BUF_BUDDY_STAMP_FREE); - ulint fold = buf_page_address_fold(space, offset); - rw_lock_t* hash_lock = buf_page_hash_lock_get(buf_pool, fold); + const page_id_t page_id(space, offset); + + /* If space,offset is bogus, then we know that the + buf_page_hash_get_low() call below will return NULL. */ + if (!force && buf_pool != buf_pool_get(page_id)) { + return(false); + } + + rw_lock_t* hash_lock = buf_page_hash_lock_get(buf_pool, page_id); rw_lock_x_lock(hash_lock); - bpage = buf_page_hash_get_low(buf_pool, space, offset, fold); + bpage = buf_page_hash_get_low(buf_pool, page_id); if (!bpage || bpage->zip.data != src) { /* The block has probably been freshly @@ -570,7 +599,27 @@ buf_buddy_relocate( rw_lock_x_unlock(hash_lock); - return(false); + if (!force || space != 0 || offset != 0) { + return(false); + } + + /* It might be just uninitialized page. + We should search from LRU list also. */ + + bpage = UT_LIST_GET_FIRST(buf_pool->LRU); + while (bpage != NULL) { + if (bpage->zip.data == src) { + hash_lock = buf_page_hash_lock_get( + buf_pool, bpage->id); + rw_lock_x_lock(hash_lock); + break; + } + bpage = UT_LIST_GET_NEXT(LRU, bpage); + } + + if (bpage == NULL) { + return(false); + } } if (page_zip_get_size(&bpage->zip) != size) { @@ -588,20 +637,17 @@ buf_buddy_relocate( contain uninitialized data. */ UNIV_MEM_ASSERT_W(src, size); - ib_mutex_t* block_mutex = buf_page_get_mutex(bpage); + BPageMutex* block_mutex = buf_page_get_mutex(bpage); mutex_enter(block_mutex); if (buf_page_can_relocate(bpage)) { /* Relocate the compressed page. */ - ullint usec = ut_time_us(NULL); + uintmax_t usec = ut_time_us(NULL); ut_a(bpage->zip.data == src); - /* Note: This is potentially expensive, we need a better - solution here. We go with correctness for now. */ - ::memcpy(dst, src, size); - + memcpy(dst, src, size); bpage->zip.data = reinterpret_cast<page_zip_t*>(dst); rw_lock_x_unlock(hash_lock); @@ -612,24 +658,19 @@ buf_buddy_relocate( reinterpret_cast<buf_buddy_free_t*>(src), i); buf_buddy_stat_t* buddy_stat = &buf_pool->buddy_stat[i]; - - ++buddy_stat->relocated; - + buddy_stat->relocated++; buddy_stat->relocated_usec += ut_time_us(NULL) - usec; - return(true); } rw_lock_x_unlock(hash_lock); mutex_exit(block_mutex); - return(false); } /**********************************************************************//** Deallocate a block. */ -UNIV_INTERN void buf_buddy_free_low( /*===============*/ @@ -663,7 +704,8 @@ recombine: /* Do not recombine blocks if there are few free blocks. We may waste up to 15360*max_len bytes to free blocks (1024 + 2048 + 4096 + 8192 = 15360) */ - if (UT_LIST_GET_LEN(buf_pool->zip_free[i]) < 16) { + if (UT_LIST_GET_LEN(buf_pool->zip_free[i]) < 16 + && buf_pool->curr_size >= buf_pool->old_size) { goto func_exit; } @@ -684,7 +726,7 @@ buddy_is_free: goto recombine; case BUF_BUDDY_STATE_USED: - ut_d(BUF_BUDDY_LIST_VALIDATE(buf_pool, i)); + ut_d(buf_buddy_list_validate(buf_pool, i)); /* The buddy is not free. Is there a free block of this size? */ @@ -698,7 +740,8 @@ buddy_is_free: /* Try to relocate the buddy of buf to the free block. */ - if (buf_buddy_relocate(buf_pool, buddy, zip_buf, i)) { + if (buf_buddy_relocate(buf_pool, buddy, zip_buf, i, + false)) { goto buddy_is_free; } @@ -719,3 +762,119 @@ func_exit: reinterpret_cast<buf_buddy_free_t*>(buf), i); } + +/** Reallocate a block. +@param[in] buf_pool buffer pool instance +@param[in] buf block to be reallocated, must be pointed +to by the buffer pool +@param[in] size block size, up to UNIV_PAGE_SIZE +@retval false if failed because of no free blocks. */ +bool +buf_buddy_realloc( + buf_pool_t* buf_pool, + void* buf, + ulint size) +{ + buf_block_t* block = NULL; + ulint i = buf_buddy_get_slot(size); + + ut_ad(buf_pool_mutex_own(buf_pool)); + ut_ad(!mutex_own(&buf_pool->zip_mutex)); + ut_ad(i <= BUF_BUDDY_SIZES); + ut_ad(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN)); + + if (i < BUF_BUDDY_SIZES) { + /* Try to allocate from the buddy system. */ + block = reinterpret_cast<buf_block_t*>( + buf_buddy_alloc_zip(buf_pool, i)); + } + + if (block == NULL) { + /* Try allocating from the buf_pool->free list. */ + block = buf_LRU_get_free_only(buf_pool); + + if (block == NULL) { + return(false); /* free_list was not enough */ + } + + buf_buddy_block_register(block); + + block = reinterpret_cast<buf_block_t*>( + buf_buddy_alloc_from( + buf_pool, block->frame, i, BUF_BUDDY_SIZES)); + } + + buf_pool->buddy_stat[i].used++; + + /* Try to relocate the buddy of buf to the free block. */ + if (buf_buddy_relocate(buf_pool, buf, block, i, true)) { + /* succeeded */ + buf_buddy_free_low(buf_pool, buf, i); + } else { + /* failed */ + buf_buddy_free_low(buf_pool, block, i); + } + + return(true); /* free_list was enough */ +} + +/** Combine all pairs of free buddies. +@param[in] buf_pool buffer pool instance */ +void +buf_buddy_condense_free( + buf_pool_t* buf_pool) +{ + ut_ad(buf_pool_mutex_own(buf_pool)); + ut_ad(buf_pool->curr_size < buf_pool->old_size); + + for (ulint i = 0; i < UT_ARR_SIZE(buf_pool->zip_free); ++i) { + buf_buddy_free_t* buf = + UT_LIST_GET_FIRST(buf_pool->zip_free[i]); + + /* seek to withdraw target */ + while (buf != NULL + && !buf_frame_will_withdrawn( + buf_pool, reinterpret_cast<byte*>(buf))) { + buf = UT_LIST_GET_NEXT(list, buf); + } + + while (buf != NULL) { + buf_buddy_free_t* next = + UT_LIST_GET_NEXT(list, buf); + + buf_buddy_free_t* buddy = + reinterpret_cast<buf_buddy_free_t*>( + buf_buddy_get( + reinterpret_cast<byte*>(buf), + BUF_BUDDY_LOW << i)); + + /* seek to the next withdraw target */ + while (true) { + while (next != NULL + && !buf_frame_will_withdrawn( + buf_pool, + reinterpret_cast<byte*>(next))) { + next = UT_LIST_GET_NEXT(list, next); + } + + if (buddy != next) { + break; + } + + next = UT_LIST_GET_NEXT(list, next); + } + + if (buf_buddy_is_free(buddy, i) + == BUF_BUDDY_STATE_FREE) { + /* Both buf and buddy are free. + Try to combine them. */ + buf_buddy_remove_from_free(buf_pool, buf, i); + buf_pool->buddy_stat[i].used++; + + buf_buddy_free_low(buf_pool, buf, i); + } + + buf = next; + } + } +} |