Skip to content

Commit 01f9c81

Browse files
committed
MDEV-29336: Potential deadlock in btr_page_alloc_low() with the AHI
The index root page contains the fields BTR_SEG_TOP and BTR_SEG_LEAF which keep track of allocated pages in the index tree. These fields are normally protected by an Update latch, so that concurrent read access to other parts of the page will be possible. When the index root page is already exclusively latched in the mini-transaction, we must not try to acquire a lower-grade Update latch. In fact, when the root page is already X or U latched in the mini-transaction, there is no point to acquire another latch. Moreover, after a U latch was acquired on top of an X-latch, mtr_t::defer_drop_ahi() would trigger an assertion failure or lock corruption in block->page.lock.u_x_upgrade() because X locks already exist on the block. This problem may have been introduced in commit 03ca649 (MDEV-24142). btr_page_alloc_low(), btr_page_free(): Initially buffer-fix the root page. If it is already U or X latched, release the buffer-fix. Else, upgrade the buffer-fix to a U latch. mtr_t::u_lock_register(): Upgrade a buffer-fix to U latch. mtr_t::have_u_or_x_latch(): Check if U or X latches are already registered in the mini-transaction.
1 parent fbb2b1f commit 01f9c81

File tree

3 files changed

+79
-5
lines changed

3 files changed

+79
-5
lines changed

storage/innobase/btr/btr0btr.cc

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -513,9 +513,28 @@ btr_page_alloc_low(
513513
page should be initialized. */
514514
dberr_t* err)/*!< out: error code */
515515
{
516-
buf_block_t *root= btr_root_block_get(index, RW_SX_LATCH, mtr, err);
516+
const auto savepoint= mtr->get_savepoint();
517+
buf_block_t *root= btr_root_block_get(index, RW_NO_LATCH, mtr, err);
517518
if (UNIV_UNLIKELY(!root))
518519
return root;
520+
521+
if (mtr->have_u_or_x_latch(*root))
522+
{
523+
#ifdef BTR_CUR_HASH_ADAPT
524+
ut_ad(!root->index || !root->index->freed());
525+
#endif
526+
mtr->release_block_at_savepoint(savepoint, root);
527+
}
528+
else
529+
{
530+
mtr->u_lock_register(savepoint);
531+
root->page.lock.u_lock();
532+
#ifdef BTR_CUR_HASH_ADAPT
533+
if (root->index)
534+
mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX);
535+
#endif
536+
}
537+
519538
fseg_header_t *seg_header= root->page.frame +
520539
(level ? PAGE_HEADER + PAGE_BTR_SEG_TOP : PAGE_HEADER + PAGE_BTR_SEG_LEAF);
521540
return fseg_alloc_free_page_general(seg_header, hint_page_no, file_direction,
@@ -610,11 +629,30 @@ dberr_t btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr,
610629

611630
fil_space_t *space= index->table->space;
612631
dberr_t err;
613-
if (page_t* root = btr_root_get(index, mtr, &err))
632+
633+
const auto savepoint= mtr->get_savepoint();
634+
if (buf_block_t *root= btr_root_block_get(index, RW_NO_LATCH, mtr, &err))
614635
{
615-
err= fseg_free_page(&root[blob || page_is_leaf(block->page.frame)
616-
? PAGE_HEADER + PAGE_BTR_SEG_LEAF
617-
: PAGE_HEADER + PAGE_BTR_SEG_TOP],
636+
if (mtr->have_u_or_x_latch(*root))
637+
{
638+
#ifdef BTR_CUR_HASH_ADAPT
639+
ut_ad(!root->index || !root->index->freed());
640+
#endif
641+
mtr->release_block_at_savepoint(savepoint, root);
642+
}
643+
else
644+
{
645+
mtr->u_lock_register(savepoint);
646+
root->page.lock.u_lock();
647+
#ifdef BTR_CUR_HASH_ADAPT
648+
if (root->index)
649+
mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX);
650+
#endif
651+
}
652+
err= fseg_free_page(&root->page.frame[blob ||
653+
page_is_leaf(block->page.frame)
654+
? PAGE_HEADER + PAGE_BTR_SEG_LEAF
655+
: PAGE_HEADER + PAGE_BTR_SEG_TOP],
618656
space, page, mtr, space_latched);
619657
}
620658
if (err == DB_SUCCESS)

storage/innobase/include/mtr0mtr.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ struct mtr_t {
177177
@param block buffer pool block to search for */
178178
bool have_x_latch(const buf_block_t &block) const;
179179

180+
/** Check if we are holding a block latch in S or U mode
181+
@param block buffer pool block to search for */
182+
bool have_u_or_x_latch(const buf_block_t &block) const;
183+
180184
/** Copy the tablespaces associated with the mini-transaction
181185
(needed for generating FILE_MODIFY records)
182186
@param[in] mtr mini-transaction that may modify
@@ -336,6 +340,15 @@ struct mtr_t {
336340
@param rw_latch RW_S_LATCH, RW_SX_LATCH, RW_X_LATCH, RW_NO_LATCH */
337341
void page_lock(buf_block_t *block, ulint rw_latch);
338342

343+
/** Register a page latch on a buffer-fixed block was buffer-fixed.
344+
@param latch latch type */
345+
void u_lock_register(ulint savepoint)
346+
{
347+
mtr_memo_slot_t *slot= m_memo.at<mtr_memo_slot_t*>(savepoint);
348+
ut_ad(slot->type == MTR_MEMO_BUF_FIX);
349+
slot->type= MTR_MEMO_PAGE_SX_FIX;
350+
}
351+
339352
/** Upgrade U locks on a block to X */
340353
void page_lock_upgrade(const buf_block_t &block);
341354
/** Upgrade X lock to X */

storage/innobase/mtr/mtr0mtr.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,21 @@ struct FindBlockX
12201220
}
12211221
};
12221222

1223+
/** Find out whether a block was not X or U latched by the mini-transaction */
1224+
struct FindBlockUX
1225+
{
1226+
const buf_block_t &block;
1227+
1228+
FindBlockUX(const buf_block_t &block): block(block) {}
1229+
1230+
/** @return whether the block was not found x-latched */
1231+
bool operator()(const mtr_memo_slot_t *slot) const
1232+
{
1233+
return slot->object != &block ||
1234+
!(slot->type & (MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX));
1235+
}
1236+
};
1237+
12231238
#ifdef UNIV_DEBUG
12241239
/** Assert that the block is not present in the mini-transaction */
12251240
struct FindNoBlock
@@ -1250,6 +1265,14 @@ bool mtr_t::have_x_latch(const buf_block_t &block) const
12501265
return true;
12511266
}
12521267

1268+
bool mtr_t::have_u_or_x_latch(const buf_block_t &block) const
1269+
{
1270+
if (m_memo.for_each_block(CIterate<FindBlockUX>(FindBlockUX(block))))
1271+
return false;
1272+
ut_ad(block.page.lock.have_u_or_x());
1273+
return true;
1274+
}
1275+
12531276
/** Check if we are holding exclusive tablespace latch
12541277
@param space tablespace to search for
12551278
@param shared whether to look for shared latch, instead of exclusive

0 commit comments

Comments
 (0)