/** When freeing a buf we attempt to coalesce by looking at its buddy anddecidingwhetheritisfreeornot.Toascertainifthebuddyis freewelookforBUF_BUDDY_STAMP_FREEatBUF_BUDDY_STAMP_OFFSET withinthebuddy.Thequestionishowwecanbesurethatitis safetolookatBUF_BUDDY_STAMP_OFFSET. Theanswerliesinfollowinginvariants: *Allblocksallocatedbybuddyallocatorareusedforcompressed pageframe. *Acompressedtablealwayshavespace_id<SRV_SPACE_ID_UPPER_BOUND *BUF_BUDDY_STAMP_OFFSETalwayspointstothespace_idfieldin aframe. --Theaboveistruebecausewelookatthesefieldswhenthe correspondingbuddyblockisfreewhichimpliesthat: *Theblockwearelookingatmusthaveanaddressalignedat thesamesizethatitsfreebuddyhas.Forexample,ifwehave afreeblockof8Kthenitsbuddy'saddressmustbealignedat 8Kaswell. *Itispossiblethattheblockwearelookingatmayhavebeen furtherdividedintosmallersizedblocksbutitsstarting addressmuststillremainthestartofapageframei.e.:it cannotbemiddleofablock.Forexample,ifwehaveafree blockofsize8Kthenitsbuddymaybedividedintoblocks of,say,1K,1K,2K,4Kbutthebuddy'saddresswillstillbe thestartingaddressoffirst1Kcompressedpage. *Whatisimportanttonoteisthatforanygivenblock,the buddy'saddresscannotbeinthemiddleofalargerblocki.e.: inaboveexample,our8Kblockcannothaveabuddywhoseaddress isalignedon8Kbutitispartofalarger16Kblock.
*/
/** Offset within buf_buddy_free_t where free or non_free stamps
are written.*/ #define BUF_BUDDY_STAMP_OFFSET FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID
/** 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_SPACE_ID_UPPER_BOUND
/** 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 0XFFFFFFFFUL
/** Return type of buf_buddy_is_free() */ enum buf_buddy_state_t {
BUF_BUDDY_STATE_FREE, /*!< If the buddy to completely free */
BUF_BUDDY_STATE_USED, /*!< Buddy currently in used */
BUF_BUDDY_STATE_PARTIALLY_USED/*!< Some sub-blocks in the buddy
are in use */
};
/**********************************************************************//**
Invalidate memory area that we won't access while page is free */
UNIV_INLINE void
buf_buddy_mem_invalid( /*==================*/
buf_buddy_free_t* buf, /*!< in: block to check */
ulint i) /*!< in: index of zip_free[] */
{
ut_ad(i <= BUF_BUDDY_SIZES);
/** Validate a buddy list.
@param[in] i buddy size to validate */ staticvoid buf_buddy_list_validate(ulint i)
{
ut_list_validate(buf_pool.zip_free[i], CheckZipFree(i));
}
/**********************************************************************//**
Debug function to validate that a buffer is indeed free i.e.: in the
zip_free[].
@param[in] buf block to check
@param[in] i index of buf_pool.zip_free[]
@returntrueif free */ staticbool buf_buddy_check_free(const buf_buddy_free_t* buf, ulint i)
{ const ulint size = BUF_BUDDY_LOW << i;
/**********************************************************************//**
Checks if a buf is free i.e.: in the zip_free[].
@retval BUF_BUDDY_STATE_FREE if fully free
@retval BUF_BUDDY_STATE_USED if currently in use
@retval BUF_BUDDY_STATE_PARTIALLY_USED if partially in use. */ static MY_ATTRIBUTE((warn_unused_result))
buf_buddy_state_t
buf_buddy_is_free( /*==============*/
buf_buddy_free_t* buf, /*!< in: block to check */
ulint i) /*!< in: index of
buf_pool.zip_free[] */
{ #ifdef UNIV_DEBUG const ulint size = BUF_BUDDY_LOW << i;
ut_ad(!ut_align_offset(buf, size));
ut_ad(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN)); #endif/* UNIV_DEBUG */
/* We assume that all memory from buf_buddy_alloc()
is used for compressed page frames. */
/* We look inside the allocated objects returned by buf_buddy_alloc()andassumethateachblockisacompressed pagethatcontainsoneofthefollowinginspace_id. *BUF_BUDDY_STAMP_FREEiftheblockisinazip_freelistor *BUF_BUDDY_STAMP_NONFREEiftheblockhasbeenallocatedbut notinitializedyetor *Avalidspace_idofacompressedtablespace
Thecallbelowattemptstoreadfromfreememory.Thememory is"owned"bythebuddyallocator(andithasbeenallocated
from the buffer pool), so there is nothing wrong about this. */ if (!buf_buddy_stamp_is_free(buf)) { return(BUF_BUDDY_STATE_USED);
}
/* A block may be free but a fragment of it may still be in use. Toguardagainstthatwewritethefreeblocksizeintermsof zip_freeindexatstartofstampedblock.Notethatwecan
safely rely on this value only if the buf is free. */
ut_ad(buf->stamp.size <= i); return(buf->stamp.size == i
? BUF_BUDDY_STATE_FREE
: BUF_BUDDY_STATE_PARTIALLY_USED);
}
/** Add a block to the head of the appropriate buddy free list. @param[in,out]bufblocktobefreed
@param[in] i index of buf_pool.zip_free[] */ staticvoid buf_buddy_add_to_free(buf_buddy_free_t *buf, ulint i)
{
mysql_mutex_assert_owner(&buf_pool.mutex);
ut_ad(buf_pool.zip_free[i].start != buf);
buf_buddy_stamp_free(buf, i);
UT_LIST_ADD_FIRST(buf_pool.zip_free[i], buf);
ut_d(buf_buddy_list_validate(i));
}
/** Remove a block from the appropriate buddy free list. @param[in,out]bufblocktobefreed
@param[in] i index of buf_pool.zip_free[] */ staticvoid buf_buddy_remove_from_free(buf_buddy_free_t *buf, ulint i)
{
mysql_mutex_assert_owner(&buf_pool.mutex);
ut_ad(buf_buddy_check_free(buf, i));
/** Try to allocate a block from buf_pool.zip_free[]. @param[in]iindexofbuf_pool.zip_free[]
@return allocated block, or NULL if buf_pool.zip_free[] was empty */ static buf_buddy_free_t* buf_buddy_alloc_zip(ulint i)
{
buf_buddy_free_t* buf;
if (size_t size = buf_pool.shrinking_size()) { while (buf != NULL
&& buf_pool.will_be_withdrawn( reinterpret_cast<byte*>(buf), size)) { /* This should be withdrawn, not to be allocated */
buf = UT_LIST_GET_NEXT(list, buf);
}
}
if (buf) {
buf_buddy_remove_from_free(buf, i);
ut_ad(!buf_pool.contains_zip(buf, BUF_BUDDY_LOW_SHIFT + i));
} elseif (i + 1 < BUF_BUDDY_SIZES) { /* Attempt to split. */
buf = buf_buddy_alloc_zip(i + 1);
/** Allocate a buffer block to the buddy allocator.
@param block buffer block to register */ staticvoid buf_buddy_block_register(buf_block_t *block) noexcept
{
ut_ad(buf_pool.is_uncompressed_current(block));
ut_ad(block->page.state() == buf_page_t::MEMORY);
ut_d(buf_buddy_n_frames++);
}
/** Allocate a block from a bigger object. @param[in]bufablockthatisfreetouse @param[in]iindexofbuf_pool.zip_free[]
@return allocated block */ staticvoid *buf_buddy_alloc_from(void *buf, ulint i)
{
ut_ad(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN));
ut_ad(i <= BUF_BUDDY_SIZES);
ut_ad(!ut_align_offset(buf, srv_page_size));
ut_ad(!buf_pool.contains_zip(buf, srv_page_size_shift));
/* Add the unused parts of the block to the free lists. */ for (ulint j = BUF_BUDDY_SIZES, offs = srv_page_size; j-- > i; ) {
buf_buddy_free_t* zip_buf;
const page_id_t page_id(space, offset); /* FIXME: we are computing this while holding buf_pool.mutex */ auto &cell= buf_pool.page_hash.cell_get(page_id.fold());
bpage = buf_pool.page_hash.get(page_id, cell);
if (!bpage || bpage->zip.data != src) { /* The block has probably been freshly allocatedbybuf_LRU_get_free_block()butnot addedtobuf_pool.page_hashyet.Obviously,
it cannot be relocated. */
if (!force || space != 0 || offset != 0) { return(false);
}
/* It might be just uninitialized page.
We should search from LRU list also. */
if (page_zip_get_size(&bpage->zip) != size) { /* The block is of different size. We would havetorelocateallblockscoveredbysrc.
For the sake of simplicity, give up. */
ut_ad(page_zip_get_size(&bpage->zip) < size); return(false);
}
/* The block must have been allocated, but it may
contain uninitialized data. */
MEM_CHECK_ADDRESSABLE(src, size);
if (!bpage->can_relocate()) { returnfalse;
}
page_hash_latch &hash_lock = buf_pool.page_hash.lock_get(cell); /* It does not make sense to use transactional_lock_guard here, becausethememcpy()of1024to16384byteswouldlikelymakethe
memory transaction too large. */
hash_lock.lock();
if (bpage->can_relocate()) { /* Relocate the compressed page. */ const ulonglong ns = my_interval_timer();
/** Deallocate a block. @param[in]bufblocktobefreed,mustnotbepointedto bythebufferpool
@param[in] i index of buf_pool.zip_free[], or BUF_BUDDY_SIZES */ void buf_buddy_free_low(void* buf, ulint i) noexcept
{
buf_buddy_free_t* buddy;
/* Do not recombine blocks if there are few free blocks. Wemaywasteupto15360*max_lenbytestofreeblocks
(1024 + 2048 + 4096 + 8192 = 15360) */ if (UT_LIST_GET_LEN(buf_pool.zip_free[i]) < 16) { goto func_exit;
}
switch (buf_buddy_is_free(buddy, i)) { case BUF_BUDDY_STATE_FREE: /* The buddy is free: recombine */
buf_buddy_remove_from_free(buddy, i);
buddy_is_free:
i++;
buf = ut_align_down(buf, BUF_BUDDY_LOW << i);
ut_ad(!buf_pool.contains_zip(buf, BUF_BUDDY_LOW_SHIFT + i)); goto recombine;
case BUF_BUDDY_STATE_USED:
ut_d(buf_buddy_list_validate(i));
/* The buddy is not free. Is there a free block of
this size? */ if (buf_buddy_free_t* zip_buf =
UT_LIST_GET_FIRST(buf_pool.zip_free[i])) {
/* Remove the block from the free list, because asuccessfulbuf_buddy_relocate()willoverwrite
zip_free->list. */
buf_buddy_remove_from_free(zip_buf, i);
/* Try to relocate the buddy of buf to the free
block. */ if (buf_buddy_relocate(buddy, zip_buf, i, false)) { goto buddy_is_free;
}
buf_buddy_add_to_free(zip_buf, i);
}
break; case BUF_BUDDY_STATE_PARTIALLY_USED: /* Some sub-blocks in the buddy are still in use.
Relocation will fail. No need to try. */ break;
}
func_exit: /* Free the block to the buddy list. */
buf_buddy_add_to_free(reinterpret_cast<buf_buddy_free_t*>(buf), i);
}
/** Reallocate a ROW_FORMAT=COMPRESSED page frame during buf_pool_t::shrink(). @parambpagepagedescriptorcoveringaROW_FORMAT=COMPRESSEDpage @paramblockuncompressedblockforstorage @returnblock
@retval nullptr if the block was consumed */
ATTRIBUTE_COLD
buf_block_t *buf_buddy_shrink(buf_page_t *bpage, buf_block_t *block) noexcept
{
ut_ad(bpage->zip.data);
/* seek to the next withdraw target */ do
{ while ((next= UT_LIST_GET_NEXT(list, next)) &&
!buf_pool.will_be_withdrawn(reinterpret_cast<byte*>(next),
size)) {}
} while (buddy == next);
if (buf_buddy_is_free(buddy, i) != BUF_BUDDY_STATE_FREE) continue;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.