Commit f619d79b authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-25491: Race condition between DROP TABLE and purge of SYS_INDEXES record

btr_free_if_exists(): Always use the BUF_GET_POSSIBLY_FREED mode
when accessing pages, because due to MDEV-24589 the function
fil_space_t::set_stopping(true) can be called at any time during
the execution of this function.

mtr_t::m_freeing_tree: New data member for debugging purposes.

buf_page_get_low(): Assert that the BUF_GET mode is not being used
anywhere during the execution of btr_free_if_exists().

In all code related to freeing or allocating pages, we will add some
robustness, by making more use of BUF_GET_POSSIBLY_FREED and by
reporting an error instead of crashing in some cases of corruption.
parent a81aec15
......@@ -939,9 +939,11 @@ static void btr_free_root(buf_block_t *block, mtr_t *mtr)
#endif /* UNIV_BTR_DEBUG */
/* Free the entire segment in small steps. */
ut_d(mtr->freeing_tree());
while (!fseg_free_step(PAGE_HEADER + PAGE_BTR_SEG_TOP + block->frame, mtr));
}
MY_ATTRIBUTE((warn_unused_result))
/** Prepare to free a B-tree.
@param[in] page_id page id
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
......@@ -949,33 +951,29 @@ static void btr_free_root(buf_block_t *block, mtr_t *mtr)
@param[in,out] mtr mini-transaction
@return root block, to invoke btr_free_but_not_root() and btr_free_root()
@retval NULL if the page is no longer a matching B-tree page */
static MY_ATTRIBUTE((warn_unused_result))
buf_block_t*
btr_free_root_check(
const page_id_t page_id,
ulint zip_size,
index_id_t index_id,
mtr_t* mtr)
static
buf_block_t *btr_free_root_check(const page_id_t page_id, ulint zip_size,
index_id_t index_id, mtr_t *mtr)
{
ut_ad(page_id.space() != SRV_TMP_SPACE_ID);
ut_ad(index_id != BTR_FREED_INDEX_ID);
buf_block_t* block = buf_page_get(
page_id, zip_size, RW_X_LATCH, mtr);
if (block) {
if (fil_page_index_page_check(block->frame)
&& index_id == btr_page_get_index_id(block->frame)) {
/* This should be a root page.
It should not be possible to reassign the same
index_id for some other index in the tablespace. */
ut_ad(!page_has_siblings(block->frame));
} else {
block = NULL;
}
}
ut_ad(page_id.space() != SRV_TMP_SPACE_ID);
ut_ad(index_id != BTR_FREED_INDEX_ID);
buf_block_t *block= buf_page_get_gen(page_id, zip_size, RW_X_LATCH,
nullptr, BUF_GET_POSSIBLY_FREED, mtr);
if (!block);
else if (block->page.status == buf_page_t::FREED)
block= nullptr;
else if (fil_page_index_page_check(block->frame) &&
index_id == btr_page_get_index_id(block->frame))
/* This should be a root page. It should not be possible to
reassign the same index_id for some other index in the
tablespace. */
ut_ad(!page_has_siblings(block->frame));
else
block= nullptr;
return(block);
return block;
}
/** Initialize the root page of the b-tree
......@@ -1131,6 +1129,7 @@ btr_free_but_not_root(
ut_ad(!page_has_siblings(block->frame));
leaf_loop:
mtr_start(&mtr);
ut_d(mtr.freeing_tree());
mtr_set_log_mode(&mtr, log_mode);
mtr.set_named_space_id(block->page.id().space());
......@@ -1230,23 +1229,20 @@ void dict_index_t::clear(que_thr_t *thr)
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
@param[in] index_id PAGE_INDEX_ID contents
@param[in,out] mtr mini-transaction */
void
btr_free_if_exists(
const page_id_t page_id,
ulint zip_size,
index_id_t index_id,
mtr_t* mtr)
void btr_free_if_exists(const page_id_t page_id, ulint zip_size,
index_id_t index_id, mtr_t *mtr)
{
buf_block_t* root = btr_free_root_check(
page_id, zip_size, index_id, mtr);
if (root == NULL) {
return;
}
btr_free_but_not_root(root, mtr->get_log_mode());
mtr->set_named_space_id(page_id.space());
btr_free_root(root, mtr);
if (fil_space_t *space= fil_space_t::get(page_id.space()))
{
if (buf_block_t *root= btr_free_root_check(page_id, zip_size, index_id,
mtr))
{
btr_free_but_not_root(root, mtr->get_log_mode());
mtr->set_named_space(space);
btr_free_root(root, mtr);
}
space->release();
}
}
/** Free an index tree in a temporary tablespace.
......
......@@ -2585,6 +2585,7 @@ buf_page_get_low(
/* fall through */
case BUF_GET:
case BUF_GET_IF_IN_POOL_OR_WATCH:
ut_ad(!mtr->is_freeing_tree());
fil_space_t* s = fil_space_get(page_id.space());
ut_ad(s);
ut_ad(s->zip_size() == zip_size);
......
......@@ -125,13 +125,16 @@ fseg_alloc_free_page_low(
@param[in] space tablespace
@param[in,out] mtr mini-transaction
@return pointer to the space header, page x-locked */
inline buf_block_t *fsp_get_header(const fil_space_t *space, mtr_t *mtr)
static buf_block_t *fsp_get_header(const fil_space_t *space, mtr_t *mtr)
{
buf_block_t *block= buf_page_get(page_id_t(space->id, 0), space->zip_size(),
RW_SX_LATCH, mtr);
ut_ad(space->id == mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID +
block->frame));
return block;
buf_block_t *block= buf_page_get_gen(page_id_t(space->id, 0),
space->zip_size(), RW_SX_LATCH,
nullptr, BUF_GET_POSSIBLY_FREED, mtr);
if (!block || block->page.status == buf_page_t::FREED)
return nullptr;
ut_ad(space->id == mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID +
block->frame));
return block;
}
/** Set the XDES_FREE_BIT of a page.
......@@ -302,8 +305,8 @@ fseg_mark_page_used(fseg_inode_t *seg_inode, buf_block_t *iblock,
@param[in,out] sp_header tablespace header page, x-latched
@param[in] space tablespace
@param[in] offset page offset
@param[out] desc_block descriptor block
@param[in,out] mtr mini-transaction
@param[out] desc_block descriptor block
@param[in] init_space whether the tablespace is being initialized
@return pointer to the extent descriptor, NULL if the page does not
exist in the space or if the offset exceeds free limit */
......@@ -313,8 +316,8 @@ xdes_get_descriptor_with_space_hdr(
buf_block_t* header,
const fil_space_t* space,
page_no_t offset,
buf_block_t** desc_block,
mtr_t* mtr,
buf_block_t** desc_block = nullptr,
bool init_space = false)
{
ut_ad(space->is_owner());
......@@ -368,15 +371,18 @@ defined, as they are uninitialized above the free limit.
@param[in] space tablespace
@param[in] offset page offset; if equal to the free limit, we
try to add new extents to the space free list
@param[out] xdes extent descriptor page
@param[in,out] mtr mini-transaction
@param[out] xdes extent descriptor page
@return the extent descriptor */
static xdes_t* xdes_get_descriptor(const fil_space_t *space, page_no_t offset,
buf_block_t **xdes, mtr_t *mtr)
static xdes_t *xdes_get_descriptor(const fil_space_t *space, page_no_t offset,
mtr_t *mtr, buf_block_t **xdes= nullptr)
{
buf_block_t *block= buf_page_get(page_id_t(space->id, 0), space->zip_size(),
RW_SX_LATCH, mtr);
return xdes_get_descriptor_with_space_hdr(block, space, offset, xdes, mtr);
buf_block_t *block= buf_page_get_gen(page_id_t(space->id, 0),
space->zip_size(), RW_SX_LATCH,
nullptr, BUF_GET_POSSIBLY_FREED, mtr);
if (!block || block->page.status == buf_page_t::FREED)
return nullptr;
return xdes_get_descriptor_with_space_hdr(block, space, offset, mtr, xdes);
}
/** Get the extent descriptor of a page.
......@@ -429,27 +435,23 @@ xdes_get_descriptor_const(
return(NULL);
}
MY_ATTRIBUTE((nonnull(3), warn_unused_result))
/** Get a pointer to the extent descriptor. The page where the
extent descriptor resides is x-locked.
@param[in] space tablespace
@param[in] lst_node file address of the list node
contained in the descriptor
@param[out] block extent descriptor block
@param[in,out] mtr mini-transaction
@param space tablespace
@param lst_node file address of the list node contained in the descriptor
@param mode BUF_GET or BUF_GET_POSSIBLY_FREED
@param mtr mini-transaction
@param block extent descriptor block
@return pointer to the extent descriptor */
MY_ATTRIBUTE((nonnull, warn_unused_result))
UNIV_INLINE
xdes_t*
xdes_lst_get_descriptor(
const fil_space_t* space,
fil_addr_t lst_node,
buf_block_t** block,
mtr_t* mtr)
static inline
xdes_t *xdes_lst_get_descriptor(const fil_space_t &space, fil_addr_t lst_node,
mtr_t *mtr, buf_block_t **block= nullptr)
{
ut_ad(mtr->memo_contains(*space));
return fut_get_ptr(space->id, space->zip_size(),
lst_node, RW_SX_LATCH, mtr, block)
- XDES_FLST_NODE;
ut_ad(mtr->memo_contains(space));
auto b= fut_get_ptr(space.id, space.zip_size(), lst_node, RW_SX_LATCH,
mtr, block);
return b ? b - XDES_FLST_NODE : nullptr;
}
/********************************************************************//**
......@@ -904,7 +906,12 @@ fsp_fill_free_list(
buf_block_t* xdes = nullptr;
xdes_t* descr = xdes_get_descriptor_with_space_hdr(
header, space, i, &xdes, mtr, init_space);
header, space, i, mtr, &xdes, init_space);
if (!descr) {
ut_ad("corruption" == 0);
return;
}
if (xdes != header && !space->full_crc32()) {
fil_block_check_type(*xdes, FIL_PAGE_TYPE_XDES, mtr);
}
......@@ -957,18 +964,26 @@ fsp_alloc_free_extent(
{
fil_addr_t first;
xdes_t* descr;
buf_block_t* desc_block = NULL;
buf_block_t* desc_block;
buf_block_t* header = fsp_get_header(space, mtr);
if (!header) {
ut_ad("corruption" == 0);
return nullptr;
}
descr = xdes_get_descriptor_with_space_hdr(
header, space, hint, &desc_block, mtr);
header, space, hint, mtr, &desc_block);
if (!descr) {
ut_ad("corruption" == 0);
return nullptr;
}
if (desc_block != header && !space->full_crc32()) {
fil_block_check_type(*desc_block, FIL_PAGE_TYPE_XDES, mtr);
}
if (descr && (xdes_get_state(descr) == XDES_FREE)) {
if (xdes_get_state(descr) == XDES_FREE) {
/* Ok, we can take this extent */
} else {
/* Take the first extent in the free list */
......@@ -985,8 +1000,12 @@ fsp_alloc_free_extent(
}
}
descr = xdes_lst_get_descriptor(space, first, &desc_block,
mtr);
descr = xdes_lst_get_descriptor(*space, first, mtr,
&desc_block);
if (!descr) {
ut_ad("corruption" == 0);
return nullptr;
}
}
flst_remove(header, FSP_HEADER_OFFSET + FSP_FREE, desc_block,
......@@ -1073,11 +1092,16 @@ fsp_alloc_free_page(
ut_d(space->modify_check(*mtr));
buf_block_t* block = fsp_get_header(space, mtr);
if (!block) {
return nullptr;
}
buf_block_t *xdes;
/* Get the hinted descriptor */
descr = xdes_get_descriptor_with_space_hdr(block, space, hint, &xdes,
mtr);
descr = xdes_get_descriptor_with_space_hdr(block, space, hint, mtr,
&xdes);
if (descr && (xdes_get_state(descr) == XDES_FREE_FRAG)) {
/* Ok, we can take this extent */
......@@ -1096,10 +1120,9 @@ fsp_alloc_free_page(
descr = fsp_alloc_free_extent(space, hint, &xdes, mtr);
if (descr == NULL) {
if (!descr) {
/* No free space left */
return(NULL);
return nullptr;
}
xdes_set_state(*xdes, descr, XDES_FREE_FRAG, mtr);
......@@ -1108,8 +1131,12 @@ fsp_alloc_free_page(
descr - xdes->frame
+ XDES_FLST_NODE), mtr);
} else {
descr = xdes_lst_get_descriptor(space, first, &xdes,
mtr);
descr = xdes_lst_get_descriptor(*space, first, mtr,
&xdes);
if (!descr) {
ut_ad("corruption" == 0);
return nullptr;
}
}
/* Reset the hint */
......@@ -1121,11 +1148,11 @@ fsp_alloc_free_page(
uint32_t free = xdes_find_free(descr, hint % FSP_EXTENT_SIZE);
if (free == FIL_NULL) {
ut_print_buf(stderr, ((byte*) descr) - 500, 1000);
putc('\n', stderr);
ut_error;
ib::error() << "Allocation metadata for file '"
<< space->chain.start->name
<< "' is corrupted";
ut_ad("corruption" == 0);
return nullptr;
}
uint32_t page_no = xdes_get_offset(descr) + free;
......@@ -1178,10 +1205,18 @@ static void fsp_free_page(fil_space_t* space, page_no_t offset, mtr_t* mtr)
/* fprintf(stderr, "Freeing page %lu in space %lu\n", page, space); */
buf_block_t* header = fsp_get_header(space, mtr);
buf_block_t* xdes= 0;
if (!header) {
ut_ad(space->is_stopping());
return;
}
buf_block_t* xdes;
descr = xdes_get_descriptor_with_space_hdr(header, space, offset,
&xdes, mtr);
descr = xdes_get_descriptor_with_space_hdr(header, space, offset, mtr,
&xdes);
if (!descr) {
ut_ad(space->is_stopping());
return;
}
state = xdes_get_state(descr);
......@@ -1263,10 +1298,17 @@ static void fsp_free_extent(fil_space_t* space, page_no_t offset, mtr_t* mtr)
ut_ad(space->is_owner());
buf_block_t *block= fsp_get_header(space, mtr);
buf_block_t *xdes= 0;
if (!block)
return;
buf_block_t *xdes;
xdes_t* descr= xdes_get_descriptor_with_space_hdr(block, space, offset, mtr,
&xdes);
if (!descr)
{
ut_ad(space->is_stopping());
return;
}
xdes_t* descr= xdes_get_descriptor_with_space_hdr(block, space, offset,
&xdes, mtr);
ut_a(xdes_get_state(descr) != XDES_FREE);
xdes_init(*xdes, descr, mtr);
......@@ -1441,6 +1483,9 @@ static void fsp_free_seg_inode(
ut_d(space->modify_check(*mtr));
buf_block_t* header = fsp_get_header(space, mtr);
if (!header) {
return;
}
ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE);
......@@ -1489,11 +1534,11 @@ fseg_inode_try_get(
inode_addr.boffset = mach_read_from_2(header + FSEG_HDR_OFFSET);
ut_ad(space == mach_read_from_4(header + FSEG_HDR_SPACE));
inode = fut_get_ptr(space, zip_size, inode_addr, RW_SX_LATCH, mtr,
block);
if (UNIV_UNLIKELY(!mach_read_from_8(inode + FSEG_ID))) {
inode = fut_get_ptr(space, zip_size, inode_addr, RW_SX_LATCH,
mtr, block);
if (UNIV_UNLIKELY(!inode)) {
} else if (UNIV_UNLIKELY(!mach_read_from_8(inode + FSEG_ID))) {
inode = NULL;
} else {
ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N)
......@@ -1519,10 +1564,9 @@ fseg_inode_get(
mtr_t* mtr,
buf_block_t** block = NULL)
{
fseg_inode_t* inode
= fseg_inode_try_get(header, space, zip_size, mtr, block);
ut_a(inode);
return(inode);
fseg_inode_t *inode= fseg_inode_try_get(header, space, zip_size, mtr, block);
ut_a(inode);
return inode;
}
/** Get the page number from the nth fragment page slot.
......@@ -1669,6 +1713,11 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr,
}
buf_block_t* header = fsp_get_header(space, mtr);
if (!header) {
ut_ad("corruption" == 0);
goto funct_exit;
}
buf_block_t* iblock;
inode = fsp_alloc_seg_inode(space, header, &iblock, mtr);
......@@ -1823,7 +1872,7 @@ fseg_fill_free_list(
for (i = 0; i < FSEG_FREE_LIST_MAX_LEN; i++) {
buf_block_t* xdes;
descr = xdes_get_descriptor(space, hint, &xdes, mtr);
descr = xdes_get_descriptor(space, hint, mtr, &xdes);
if (!descr || (XDES_FREE != xdes_get_state(descr))) {
/* We cannot allocate the desired extent: stop */
......@@ -1880,7 +1929,14 @@ fseg_alloc_free_extent(
first = flst_get_first(inode + FSEG_FREE);
descr = xdes_lst_get_descriptor(space, first, xdes, mtr);
descr = xdes_lst_get_descriptor(*space, first, mtr, xdes);
if (UNIV_UNLIKELY(!descr)) {
ib::error() << "Allocation metadata for file '"
<< space->chain.start->name
<< "' is corrupted";
ut_ad("corruption" == 0);
return nullptr;
}
} else {
/* Segment free list was empty, allocate from space */
descr = fsp_alloc_free_extent(space, 0, xdes, mtr);
......@@ -1963,15 +2019,22 @@ fseg_alloc_free_page_low(
reserved = fseg_n_reserved_pages_low(seg_inode, &used);
buf_block_t* header = fsp_get_header(space, mtr);
if (!header) {
ut_ad("corruption" == 0);
return nullptr;
}
descr = xdes_get_descriptor_with_space_hdr(header, space, hint,
&xdes, mtr);
if (descr == NULL) {
descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr,
&xdes);
if (!descr) {
/* Hint outside space or too high above free limit: reset
hint */
/* The file space header page is always allocated. */
hint = 0;
descr = xdes_get_descriptor(space, hint, &xdes, mtr);
descr = xdes_get_descriptor(space, hint, mtr, &xdes);
if (!descr) {
return nullptr;
}
}
/* In the big if-else below we look for ret_page and ret_descr */
......@@ -2066,7 +2129,15 @@ fseg_alloc_free_page_low(
return(NULL);
}
ret_descr = xdes_lst_get_descriptor(space, first, &xdes, mtr);
ret_descr = xdes_lst_get_descriptor(*space, first, mtr, &xdes);
if (!ret_descr) {
ib::error() << "Allocation metadata for file '"
<< space->chain.start->name
<< "' is corrupted";
ut_ad("corruption" == 0);
return nullptr;
}
ret_page = xdes_find_free(ret_descr);
if (ret_page == FIL_NULL) {
ut_ad(!has_done_reservation);
......@@ -2150,7 +2221,7 @@ fseg_alloc_free_page_low(
or FSEG_FREE), and the page is not yet marked as used. */
ut_d(buf_block_t* xxdes);
ut_ad(xdes_get_descriptor(space, ret_page, &xxdes, mtr)
ut_ad(xdes_get_descriptor(space, ret_page, mtr, &xxdes)
== ret_descr);
ut_ad(xdes == xxdes);
ut_ad(xdes_is_free(ret_descr, ret_page % FSP_EXTENT_SIZE));
......@@ -2252,12 +2323,16 @@ fsp_reserve_free_pages(
ut_a(!is_system_tablespace(space->id));
ut_a(size < FSP_EXTENT_SIZE);
buf_block_t* xdes;
descr = xdes_get_descriptor_with_space_hdr(header, space, 0, &xdes,
mtr);
descr = xdes_get_descriptor_with_space_hdr(header, space, 0, mtr);
if (!descr) {
return false;
}
uint32_t n_used = xdes_get_n_used(descr);
ut_a(n_used <= size);
if (n_used > size) {
ut_ad("corruption" == 0);
return false;
}
return(size >= n_used + n_pages
|| fsp_try_extend_data_file_with_pages(
......@@ -2323,6 +2398,9 @@ fsp_reserve_free_extents(
const unsigned physical_size = space->physical_size();
buf_block_t* header = fsp_get_header(space, mtr);
if (!header) {
ut_ad("corruption" == 0);
}
try_again:
uint32_t size = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SIZE
+ header->frame);
......@@ -2446,16 +2524,16 @@ fseg_free_page_low(
const uint32_t extent_size = FSP_EXTENT_SIZE;
ut_ad(ut_is_2pow(extent_size));
buf_block_t* xdes;
xdes_t* descr = xdes_get_descriptor(space, offset, &xdes, mtr);
xdes_t* descr = xdes_get_descriptor(space, offset, mtr, &xdes);
if (xdes_is_free(descr, offset & (extent_size - 1))) {
ib::fatal() << "InnoDB is trying to free page "
<< page_id_t(space->id, offset)
<< " though it is already marked as free in the"
" tablespace! The tablespace free space info is"
" corrupt. You may need to dump your tables and"
" recreate the whole database!"
<< FORCE_RECOVERY_MSG;
if (!descr || xdes_is_free(descr, offset & (extent_size - 1))) {
if (space->is_stopping()) {
return;
}
ib::error() << "Page " << offset << " in file '"
<< space->chain.start->name
<< "' is already marked as free";
return;
}
if (xdes_get_state(descr) != XDES_FSEG) {
......@@ -2483,18 +2561,12 @@ fseg_free_page_low(
seg_id = mach_read_from_8(seg_inode + FSEG_ID);
if (UNIV_UNLIKELY(descr_id != seg_id)) {
fputs("InnoDB: Dump of the tablespace extent descriptor: ",
stderr);
ut_print_buf(stderr, descr, 40);
fputs("\nInnoDB: Dump of the segment inode: ", stderr);
ut_print_buf(stderr, seg_inode, 40);
putc('\n', stderr);
ib::fatal() << "InnoDB is trying to free page "
<< page_id_t(space->id, offset)
<< ", which does not belong to segment " << descr_id
<< " but belongs to segment " << seg_id << "."
<< FORCE_RECOVERY_MSG;
ib::error() << "InnoDB is trying to free page " << offset
<< " in file '" << space->chain.start->name
<< "' which does not belong to segment "
<< descr_id
<< " but belongs to segment " << seg_id;
return;
}
byte* p_not_full = seg_inode + FSEG_NOT_FULL_N_USED;
......@@ -2538,36 +2610,29 @@ fseg_free_page_low(
@param[in] offset page number
@param[in,out] mtr mini-transaction
@param[in] have_latch whether space->x_lock() was already called */
void
fseg_free_page(
fseg_header_t* seg_header,
fil_space_t* space,
uint32_t offset,
mtr_t* mtr,
bool have_latch)
void fseg_free_page(fseg_header_t *seg_header, fil_space_t *space,
uint32_t offset, mtr_t *mtr, bool have_latch)
{
DBUG_ENTER("fseg_free_page");
fseg_inode_t* seg_inode;
buf_block_t* iblock;
if (have_latch) {
ut_ad(space->is_owner());
} else {
mtr->x_lock_space(space);
}
DBUG_LOG("fseg_free_page", "space_id: " << space->id
<< ", page_no: " << offset);
seg_inode = fseg_inode_get(seg_header, space->id, space->zip_size(),
mtr,
&iblock);
if (!space->full_crc32()) {
fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
}
fseg_free_page_low(seg_inode, iblock, space, offset, mtr);
DBUG_ENTER("fseg_free_page");
buf_block_t *iblock;
if (have_latch)
ut_ad(space->is_owner());
else
mtr->x_lock_space(space);
DBUG_PRINT("fseg_free_page",
("space_id: " ULINTPF ", page_no: %u", space->id, offset));
if (fseg_inode_t *seg_inode= fseg_inode_try_get(seg_header,
space->id, space->zip_size(),
mtr, &iblock))
{
if (!space->full_crc32())
fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
fseg_free_page_low(seg_inode, iblock, space, offset, mtr);
}
DBUG_VOID_RETURN;
DBUG_VOID_RETURN;
}
/** Determine whether a page is free.
......@@ -2619,11 +2684,13 @@ fseg_free_extent(
#endif /* BTR_CUR_HASH_ADAPT */
)
{
ut_ad(mtr != NULL);
buf_block_t* xdes;
xdes_t* descr = xdes_get_descriptor(space, page, &xdes, mtr);
xdes_t* descr = xdes_get_descriptor(space, page, mtr, &xdes);
if (!descr) {
ut_ad(space->is_stopping());
return;
}
ut_a(xdes_get_state(descr) == XDES_FSEG);
ut_a(!memcmp(descr + XDES_ID, seg_inode + FSEG_ID, 8));
......@@ -2705,8 +2772,12 @@ fseg_free_step(
const uint32_t header_page = page_get_page_no(page_align(header));
fil_space_t* space = mtr->x_lock_space(space_id);
buf_block_t* xdes;
xdes_t* descr = xdes_get_descriptor(space, header_page, &xdes, mtr);
xdes_t* descr = xdes_get_descriptor(space, header_page, mtr);
if (!descr) {
ut_ad(space->is_stopping());
DBUG_RETURN(true);
}
/* Check that the header resides on a page which has not been
freed yet */
......@@ -2715,10 +2786,13 @@ fseg_free_step(
buf_block_t* iblock;
const ulint zip_size = space->zip_size();
inode = fseg_inode_try_get(header, space_id, zip_size, mtr, &iblock);
if (space->is_stopping()) {
DBUG_RETURN(true);
}
if (inode == NULL) {
ib::info() << "Double free of inode from "
<< page_id_t(space_id, header_page);
ib::warn() << "Double free of inode from "
<< page_id_t(space_id, header_page);
DBUG_RETURN(true);
}
......@@ -2727,6 +2801,10 @@ fseg_free_step(
}
descr = fseg_get_first_extent(inode, space, mtr);
if (space->is_stopping()) {
DBUG_RETURN(true);
}
if (descr != NULL) {
/* Free the extent held by the segment */
fseg_free_extent(inode, iblock, space, xdes_get_offset(descr),
......@@ -2779,8 +2857,6 @@ fseg_free_step_not_header(
#endif /* BTR_CUR_HASH_ADAPT */
)
{
ulint n;
xdes_t* descr;
fseg_inode_t* inode;
const uint32_t space_id = page_get_space_id(page_align(header));
......@@ -2789,15 +2865,24 @@ fseg_free_step_not_header(
fil_space_t* space = mtr->x_lock_space(space_id);
buf_block_t* iblock;
inode = fseg_inode_get(header, space_id, space->zip_size(), mtr,
&iblock);
inode = fseg_inode_try_get(header, space_id, space->zip_size(),
mtr, &iblock);
if (space->is_stopping()) {
return true;
}
if (!inode) {
ib::warn() << "Double free of "
<< page_id_t(space_id,
page_get_page_no(page_align(header)));
return true;
}
if (!space->full_crc32()) {
fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
}
descr = fseg_get_first_extent(inode, space, mtr);
if (descr != NULL) {
if (xdes_t* descr = fseg_get_first_extent(inode, space, mtr)) {
/* Free the extent held by the segment */
fseg_free_extent(inode, iblock, space, xdes_get_offset(descr),
mtr
......@@ -2810,7 +2895,7 @@ fseg_free_step_not_header(
/* Free a frag page */
n = fseg_find_last_used_frag_page_slot(inode);
ulint n = fseg_find_last_used_frag_page_slot(inode);
ut_a(n != ULINT_UNDEFINED);
......@@ -2856,15 +2941,15 @@ fseg_get_first_extent(
} else if (flst_get_len(inode + FSEG_FREE) > 0) {
first = flst_get_first(inode + FSEG_FREE);
} else {
return(NULL);
return nullptr;
}
DBUG_ASSERT(first.page != FIL_NULL);
buf_block_t *xdes;
if (first.page == FIL_NULL) {
ut_ad("corruption" == 0);
return nullptr;
}
return(first.page == FIL_NULL ? NULL
: xdes_lst_get_descriptor(space, first, &xdes, mtr));
return xdes_lst_get_descriptor(*space, first, mtr);
}
#ifdef UNIV_BTR_PRINT
......
/*****************************************************************************
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, 2020, MariaDB Corporation.
Copyright (c) 2019, 2021, MariaDB Corporation.
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
......@@ -150,10 +150,10 @@ static void flst_insert_after(buf_block_t *base, uint16_t boffset,
else
{
buf_block_t *block;
flst_node_t *next= fut_get_ptr(add->page.id().space(), add->zip_size(),
next_addr, RW_SX_LATCH, mtr, &block);
flst_write_addr(*block, next + FLST_PREV,
add->page.id().page_no(), aoffset, mtr);
if (flst_node_t *next= fut_get_ptr(add->page.id().space(), add->zip_size(),
next_addr, RW_SX_LATCH, mtr, &block))
flst_write_addr(*block, next + FLST_PREV,
add->page.id().page_no(), aoffset, mtr);
}
flst_write_addr(*cur, cur->frame + coffset + FLST_NEXT,
......@@ -201,10 +201,10 @@ static void flst_insert_before(buf_block_t *base, uint16_t boffset,
else
{
buf_block_t *block;
flst_node_t *prev= fut_get_ptr(add->page.id().space(), add->zip_size(),
prev_addr, RW_SX_LATCH, mtr, &block);
flst_write_addr(*block, prev + FLST_NEXT,
add->page.id().page_no(), aoffset, mtr);
if (flst_node_t *prev= fut_get_ptr(add->page.id().space(), add->zip_size(),
prev_addr, RW_SX_LATCH, mtr, &block))
flst_write_addr(*block, prev + FLST_NEXT,
add->page.id().page_no(), aoffset, mtr);
}
flst_write_addr(*cur, cur->frame + coffset + FLST_PREV,
......@@ -254,9 +254,10 @@ void flst_add_last(buf_block_t *base, uint16_t boffset,
? add->frame + addr.boffset
: fut_get_ptr(add->page.id().space(), add->zip_size(), addr,
RW_SX_LATCH, mtr, &cur);
flst_insert_after(base, boffset, cur,
static_cast<uint16_t>(c - cur->frame),
add, aoffset, mtr);
if (c)
flst_insert_after(base, boffset, cur,
static_cast<uint16_t>(c - cur->frame),
add, aoffset, mtr);
}
}
......@@ -287,9 +288,10 @@ void flst_add_first(buf_block_t *base, uint16_t boffset,
? add->frame + addr.boffset
: fut_get_ptr(add->page.id().space(), add->zip_size(), addr,
RW_SX_LATCH, mtr, &cur);
flst_insert_before(base, boffset, cur,
static_cast<uint16_t>(c - cur->frame),
add, aoffset, mtr);
if (c)
flst_insert_before(base, boffset, cur,
static_cast<uint16_t>(c - cur->frame),
add, aoffset, mtr);
}
}
......@@ -318,12 +320,12 @@ void flst_remove(buf_block_t *base, uint16_t boffset,
else
{
buf_block_t *block= cur;
flst_node_t *prev= prev_addr.page == cur->page.id().page_no()
? cur->frame + prev_addr.boffset
: fut_get_ptr(cur->page.id().space(), cur->zip_size(), prev_addr,
RW_SX_LATCH, mtr, &block);
flst_write_addr(*block, prev + FLST_NEXT,
next_addr.page, next_addr.boffset, mtr);
if (flst_node_t *prev= prev_addr.page == cur->page.id().page_no()
? cur->frame + prev_addr.boffset
: fut_get_ptr(cur->page.id().space(), cur->zip_size(), prev_addr,
RW_SX_LATCH, mtr, &block))
flst_write_addr(*block, prev + FLST_NEXT,
next_addr.page, next_addr.boffset, mtr);
}
if (next_addr.page == FIL_NULL)
......@@ -332,12 +334,12 @@ void flst_remove(buf_block_t *base, uint16_t boffset,
else
{
buf_block_t *block= cur;
flst_node_t *next= next_addr.page == cur->page.id().page_no()
? cur->frame + next_addr.boffset
: fut_get_ptr(cur->page.id().space(), cur->zip_size(), next_addr,
RW_SX_LATCH, mtr, &block);
flst_write_addr(*block, next + FLST_PREV,
prev_addr.page, prev_addr.boffset, mtr);
if (flst_node_t *next= next_addr.page == cur->page.id().page_no()
? cur->frame + next_addr.boffset
: fut_get_ptr(cur->page.id().space(), cur->zip_size(), next_addr,
RW_SX_LATCH, mtr, &block))
flst_write_addr(*block, next + FLST_PREV,
prev_addr.page, prev_addr.boffset, mtr);
}
byte *len= &base->frame[boffset + FLST_LEN];
......@@ -369,6 +371,7 @@ void flst_validate(const buf_block_t *base, uint16_t boffset, mtr_t *mtr)
const flst_node_t *node= fut_get_ptr(base->page.id().space(),
base->zip_size(), addr,
RW_SX_LATCH, &mtr2);
ut_ad(node);
addr= flst_get_next_addr(node);
mtr2.commit();
}
......@@ -383,6 +386,7 @@ void flst_validate(const buf_block_t *base, uint16_t boffset, mtr_t *mtr)
const flst_node_t *node= fut_get_ptr(base->page.id().space(),
base->zip_size(), addr,
RW_SX_LATCH, &mtr2);
ut_ad(node);
addr= flst_get_prev_addr(node);
mtr2.commit();
}
......
/*****************************************************************************
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
Copyright (c) 2019, 2021, MariaDB Corporation.
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
......@@ -39,7 +39,7 @@ Created 12/13/1995 Heikki Tuuri
@param[in,out] mtr mini-transaction
@return pointer to a byte in (*ptr_block)->frame; the *ptr_block is
bufferfixed and latched */
UNIV_INLINE
inline
byte*
fut_get_ptr(
ulint space,
......@@ -57,10 +57,15 @@ fut_get_ptr(
|| (rw_latch == RW_X_LATCH)
|| (rw_latch == RW_SX_LATCH));
block = buf_page_get(page_id_t(space, addr.page), zip_size,
rw_latch, mtr);
ptr = buf_block_get_frame(block) + addr.boffset;
block = buf_page_get_gen(page_id_t(space, addr.page), zip_size,
rw_latch, nullptr, BUF_GET_POSSIBLY_FREED,
mtr);
if (!block) {
} else if (block->page.status == buf_page_t::FREED) {
block = nullptr;
} else {
ptr = buf_block_get_frame(block) + addr.boffset;
}
if (ptr_block != NULL) {
*ptr_block = block;
......
......@@ -633,11 +633,17 @@ struct mtr_t {
{ ut_ad(!m_commit || m_start); return m_start && !m_commit; }
/** @return whether the mini-transaction has been committed */
bool has_committed() const { ut_ad(!m_commit || m_start); return m_commit; }
/** @return whether the mini-transaction is freeing an index tree */
bool is_freeing_tree() const { return m_freeing_tree; }
/** Notify that the mini-transaction is freeing an index tree */
void freeing_tree() { m_freeing_tree= true; }
private:
/** whether start() has been called */
bool m_start= false;
/** whether commit() has been called */
bool m_commit= false;
/** whether freeing_tree() has been called */
bool m_freeing_tree= false;
#endif
/** The page of the most recent m_log record written, or NULL */
......
......@@ -362,6 +362,7 @@ void mtr_t::start()
ut_d(m_start= true);
ut_d(m_commit= false);
ut_d(m_freeing_tree= false);
m_last= nullptr;
m_last_offset= 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment