diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2020-06-04 12:29:32 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2020-06-05 20:55:08 +0300 |
commit | f8f7595d5c523fa27832eebe8bfefe15ad5731ac (patch) | |
tree | 492038e96196f2959d00b461bf127b82a02b7229 | |
parent | ba9ad7819c26713041bc2c9e1606d5ae094e3759 (diff) | |
download | mariadb-git-f8f7595d5c523fa27832eebe8bfefe15ad5731ac.tar.gz |
MDEV-22790 Race between btr_page_mtr_lock() dropping AHI on the same block
This race condition was introduced by
commit ad6171b91cac33e70bb28fa6865488b2c65e858c (MDEV-22456).
In the observed case, two threads were executing
btr_search_drop_page_hash_index() on the same block,
to free a stale entry that was attached to a dropped index.
Both threads were holding an S latch on the block.
We must prevent the double-free of block->index by holding
block->lock in exclusive mode.
btr_search_guess_on_hash(): Do not invoke
btr_search_drop_page_hash_index(block) to get rid of
stale entries, because we are not necessarily holding
an exclusive block->lock here.
buf_defer_drop_ahi(): New function, to safely drop stale
entries in buf_page_mtr_lock(). We will skip the call to
btr_search_drop_page_hash_index(block) when only requesting
bufferfixing (no page latch), because in that case, we should
not be accessing the adaptive hash index, and we might get
a deadlock if we acquired the page latch.
-rw-r--r-- | storage/innobase/btr/btr0sea.cc | 1 | ||||
-rw-r--r-- | storage/innobase/buf/buf0buf.cc | 42 |
2 files changed, 41 insertions, 2 deletions
diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index 76aabf287c0..ef5d889190a 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -1016,7 +1016,6 @@ fail: buf_block_dbg_add_level(block, SYNC_TREE_NODE_FROM_HASH); if (UNIV_UNLIKELY(fail)) { - btr_search_drop_page_hash_index(block); goto fail_and_release_page; } } else if (UNIV_UNLIKELY(index != block->index diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 8719af6defa..cbf2ac0e296 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -4130,6 +4130,46 @@ buf_wait_for_read( } } +#ifdef BTR_CUR_HASH_ADAPT +/** If a stale adaptive hash index exists on the block, drop it. +Multiple executions of btr_search_drop_page_hash_index() on the +same block must be prevented by exclusive page latch. */ +ATTRIBUTE_COLD +static void buf_defer_drop_ahi(buf_block_t *block, mtr_memo_type_t fix_type) +{ + switch (fix_type) { + case MTR_MEMO_BUF_FIX: + /* We do not drop the adaptive hash index, because safely doing + so would require acquiring block->lock, and that is not safe + to acquire in some RW_NO_LATCH access paths. Those code paths + should have no business accessing the adaptive hash index anyway. */ + break; + case MTR_MEMO_PAGE_S_FIX: + /* Temporarily release our S-latch. */ + rw_lock_s_unlock(&block->lock); + rw_lock_x_lock(&block->lock); + if (dict_index_t *index= block->index) + if (index->freed()) + btr_search_drop_page_hash_index(block); + rw_lock_x_unlock(&block->lock); + rw_lock_s_lock(&block->lock); + break; + case MTR_MEMO_PAGE_SX_FIX: + rw_lock_sx_unlock(&block->lock); + rw_lock_x_lock(&block->lock); + if (dict_index_t *index= block->index) + if (index->freed()) + btr_search_drop_page_hash_index(block); + rw_lock_x_unlock(&block->lock); + rw_lock_sx_lock(&block->lock); + break; + default: + ut_ad(fix_type == MTR_MEMO_PAGE_X_FIX); + btr_search_drop_page_hash_index(block); + } +} +#endif /* BTR_CUR_HASH_ADAPT */ + /** Lock the page with the given latch type. @param[in,out] block block to be locked @param[in] rw_latch RW_S_LATCH, RW_X_LATCH, RW_NO_LATCH @@ -4168,7 +4208,7 @@ static buf_block_t* buf_page_mtr_lock(buf_block_t *block, { dict_index_t *index= block->index; if (index && index->freed()) - btr_search_drop_page_hash_index(block); + buf_defer_drop_ahi(block, fix_type); } #endif /* BTR_CUR_HASH_ADAPT */ |