mariadb/buf/buf0buddy.c
2006-12-11 09:59:06 +00:00

317 lines
7.4 KiB
C

/******************************************************
Binary buddy allocator for compressed pages
(c) 2006 Innobase Oy
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"
#include "page0page.h"
/**************************************************************************
Try to allocate a block from buf_pool->zip_free[]. */
void*
buf_buddy_alloc_low(
/*================*/
/* out: allocated block, or NULL
if buf_pool->zip_free[] was empty */
ulint i, /* in: index of buf_pool->zip_free[] */
ibool split) /* in: TRUE=attempt splitting,
FALSE=try to allocate exact size */
{
buf_page_t* bpage;
#ifdef UNIV_SYNC_DEBUG
ut_a(mutex_own(&buf_pool->mutex));
#endif /* UNIV_SYNC_DEBUG */
ut_a(i < BUF_BUDDY_SIZES);
bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
if (bpage) {
ut_a(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
UT_LIST_REMOVE(list, buf_pool->zip_free[i], bpage);
} else if (split && i + 1 < BUF_BUDDY_SIZES) {
bpage = buf_buddy_alloc_low(i + 1, split);
if (bpage) {
buf_page_t* buddy = bpage + (BUF_BUDDY_LOW << i);
UT_LIST_ADD_FIRST(list, buf_pool->zip_free[i], buddy);
}
}
return(bpage);
}
/**************************************************************************
Deallocate a buffer frame of UNIV_PAGE_SIZE. */
static
void
buf_buddy_free_block(
/*=================*/
void* buf) /* in: buffer frame to deallocate */
{
ulint fold = (ulint) buf / UNIV_PAGE_SIZE;
buf_page_t* bpage;
buf_block_t* block;
#ifdef UNIV_SYNC_DEBUG
ut_a(mutex_own(&buf_pool->mutex));
#endif /* UNIV_SYNC_DEBUG */
ut_a(buf == ut_align_down(buf, UNIV_PAGE_SIZE));
HASH_SEARCH(hash, buf_pool->zip_hash, fold, bpage,
((buf_block_t*) bpage)->frame == buf);
ut_a(bpage);
ut_a(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY);
block = (buf_block_t*) bpage;
mutex_enter(&block->mutex);
buf_LRU_block_free_non_file_page(block);
mutex_exit(&block->mutex);
}
/**************************************************************************
Try to relocate a block. */
static
ibool
buf_buddy_relocate(
/*===============*/
/* out: TRUE if relocated */
const void* src, /* in: block to relocate */
void* dst, /* in: free block to relocate to */
ulint i) /* in: index of buf_pool->zip_free[] */
{
buf_page_t* bpage;
const ulint size = BUF_BUDDY_LOW << i;
#ifdef UNIV_SYNC_DEBUG
ut_a(mutex_own(&buf_pool->mutex));
#endif /* UNIV_SYNC_DEBUG */
ut_ad(src == ut_align_down(src, size));
ut_ad(dst == ut_align_down(dst, size));
ut_ad((((ulint) src) ^ ((ulint) dst)) == size);
/* We assume that all memory from buf_buddy_alloc()
is used for either compressed pages or buf_page_t
objects covering compressed pages. */
if (size >= PAGE_ZIP_MIN_SIZE) {
/* This is a compressed page. */
mutex_t* mutex;
bpage = buf_page_hash_get(page_get_space_id(src),
page_get_page_no(src));
ut_a(bpage);
mutex = buf_page_get_mutex(bpage);
mutex_enter(mutex);
if (buf_flush_ready_for_replace(bpage)) {
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_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
case BUF_BLOCK_FILE_PAGE:
/* Relocate the compressed page. */
ut_a(bpage->zip.data == src);
memcpy(dst, src, size);
bpage->zip.data = dst;
mutex_exit(mutex);
return(TRUE);
}
}
mutex_exit(mutex);
} else {
/* This must be a buf_page_t object. */
bpage = (buf_page_t*) src;
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_FILE_PAGE:
case BUF_BLOCK_MEMORY:
case BUF_BLOCK_REMOVE_HASH:
ut_error;
break;
case BUF_BLOCK_ZIP_DIRTY:
/* Cannot relocate dirty pages. */
break;
case BUF_BLOCK_ZIP_PAGE:
mutex_enter(&buf_pool->zip_mutex);
if (buf_flush_ready_for_replace(bpage)) {
buf_page_t* dpage = (buf_page_t*) dst;
buf_page_t* b;
ulint fold
= buf_page_address_fold(bpage->space,
bpage->offset);
memcpy(dpage, bpage, size);
/* relocate buf_pool->LRU */
b = UT_LIST_GET_PREV(LRU, bpage);
UT_LIST_REMOVE(LRU, buf_pool->LRU, bpage);
if (b) {
UT_LIST_INSERT_AFTER(
LRU, buf_pool->LRU, b, dpage);
} else {
UT_LIST_ADD_FIRST(
LRU, buf_pool->LRU, dpage);
}
/* relocate buf_pool->zip_clean */
b = UT_LIST_GET_PREV(list, bpage);
UT_LIST_REMOVE(list, buf_pool->zip_clean,
bpage);
if (b) {
UT_LIST_INSERT_AFTER(
list, buf_pool->zip_clean,
b, dpage);
} else {
UT_LIST_ADD_FIRST(
list, buf_pool->zip_clean,
dpage);
}
/* relocate buf_pool->page_hash */
HASH_DELETE(buf_page_t, hash,
buf_pool->page_hash, fold, bpage);
HASH_INSERT(buf_page_t, hash,
buf_pool->page_hash, fold, dpage);
}
mutex_exit(&buf_pool->zip_mutex);
return(TRUE);
}
}
return(FALSE);
}
/**************************************************************************
Deallocate a block. */
void
buf_buddy_free_low(
/*===============*/
void* buf, /* in: block to free */
ulint i) /* in: index of buf_pool->zip_free[] */
{
buf_page_t* bpage;
buf_page_t* buddy;
#ifdef UNIV_SYNC_DEBUG
ut_a(mutex_own(&buf_pool->mutex));
#endif /* UNIV_SYNC_DEBUG */
recombine:
ut_ad(i < BUF_BUDDY_SIZES);
ut_ad(buf == ut_align_down(buf, BUF_BUDDY_LOW << i));
/* Try to combine adjacent blocks. */
buddy = (buf_page_t*) buf_buddy_get(((byte*) buf), BUF_BUDDY_LOW << i);
for (bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
bpage; bpage = UT_LIST_GET_NEXT(list, bpage)) {
ut_ad(bpage->in_LRU_list);
#ifdef UNIV_DEBUG
switch (buf_page_get_state(bpage)) {
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
goto state_ok;
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
case BUF_BLOCK_FILE_PAGE:
case BUF_BLOCK_MEMORY:
case BUF_BLOCK_REMOVE_HASH:
break;
}
ut_error;
state_ok:
#endif /* UNIV_DEBUG */
if (bpage == buddy) {
buddy_free:
/* The buddy is free: recombine */
UT_LIST_REMOVE(list, buf_pool->zip_free[i], bpage);
buf = ut_align_down(buf, BUF_BUDDY_LOW << i);
if (++i < BUF_BUDDY_SIZES) {
goto recombine;
}
/* The whole block is free. */
buf_buddy_free_block(buf);
return;
}
ut_a(bpage != buf);
}
/* The buddy is not free. Is there a free block of this size? */
bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
if (bpage) {
/* Try to relocate the buddy of buf to the free block. */
if (buf_buddy_relocate(buddy, bpage, i)) {
goto buddy_free;
}
/* Try to relocate the buddy of the free block to buf. */
buddy = buf_buddy_get(bpage, BUF_BUDDY_LOW << i);
#ifdef UNIV_DEBUG
{
const buf_page_t* b;
/* The buddy must not be free, because we always
recombine adjacent free blocks. */
for (b = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
b; b = UT_LIST_GET_NEXT(list, b)) {
ut_a(b != buddy);
}
}
#endif /* UNIV_DEBUG */
if (buf_buddy_relocate(buddy, buf, i)) {
goto buddy_free;
}
}
/* Free the block to the buddy list. */
bpage = buf;
bpage->state = BUF_BLOCK_ZIP_FREE;
UT_LIST_ADD_FIRST(list, buf_pool->zip_free[i], bpage);
}