Commit ed313e8a authored by Sergey Vojtovich's avatar Sergey Vojtovich

MDEV-7148 - Recurring: InnoDB: Failing assertion: !lock->recursive

On PPC64 high-loaded server may crash due to assertion failure in InnoDB
rwlocks code.

This happened because load order between "recursive" and "writer_thread"
wasn't properly enforced.
parent df20184c
...@@ -438,6 +438,7 @@ rw_lock_x_lock_func_nowait( ...@@ -438,6 +438,7 @@ rw_lock_x_lock_func_nowait(
os_thread_id_t curr_thread = os_thread_get_curr_id(); os_thread_id_t curr_thread = os_thread_get_curr_id();
ibool success; ibool success;
ibool local_recursive= lock->recursive;
#ifdef INNODB_RW_LOCKS_USE_ATOMICS #ifdef INNODB_RW_LOCKS_USE_ATOMICS
success = os_compare_and_swap_lint(&lock->lock_word, X_LOCK_DECR, 0); success = os_compare_and_swap_lint(&lock->lock_word, X_LOCK_DECR, 0);
...@@ -452,10 +453,14 @@ rw_lock_x_lock_func_nowait( ...@@ -452,10 +453,14 @@ rw_lock_x_lock_func_nowait(
mutex_exit(&(lock->mutex)); mutex_exit(&(lock->mutex));
#endif #endif
/* Note: recursive must be loaded before writer_thread see
comment for rw_lock_set_writer_id_and_recursion_flag().
To achieve this we load it before os_compare_and_swap_lint(),
which implies full memory barrier in current implementation. */
if (success) { if (success) {
rw_lock_set_writer_id_and_recursion_flag(lock, TRUE); rw_lock_set_writer_id_and_recursion_flag(lock, TRUE);
} else if (lock->recursive } else if (local_recursive
&& os_thread_eq(lock->writer_thread, curr_thread)) { && os_thread_eq(lock->writer_thread, curr_thread)) {
/* Relock: this lock_word modification is safe since no other /* Relock: this lock_word modification is safe since no other
threads can modify (lock, unlock, or reserve) lock_word while threads can modify (lock, unlock, or reserve) lock_word while
......
...@@ -566,6 +566,7 @@ rw_lock_x_lock_low( ...@@ -566,6 +566,7 @@ rw_lock_x_lock_low(
ulint line) /*!< in: line where requested */ ulint line) /*!< in: line where requested */
{ {
os_thread_id_t curr_thread = os_thread_get_curr_id(); os_thread_id_t curr_thread = os_thread_get_curr_id();
ibool local_recursive= lock->recursive;
if (rw_lock_lock_word_decr(lock, X_LOCK_DECR)) { if (rw_lock_lock_word_decr(lock, X_LOCK_DECR)) {
...@@ -586,10 +587,12 @@ rw_lock_x_lock_low( ...@@ -586,10 +587,12 @@ rw_lock_x_lock_low(
file_name, line); file_name, line);
} else { } else {
if (!pass) /* Decrement failed: relock or failed lock
os_rmb; Note: recursive must be loaded before writer_thread see
/* Decrement failed: relock or failed lock */ comment for rw_lock_set_writer_id_and_recursion_flag().
if (!pass && lock->recursive To achieve this we load it before rw_lock_lock_word_decr(),
which implies full memory barrier in current implementation. */
if (!pass && local_recursive
&& os_thread_eq(lock->writer_thread, curr_thread)) { && os_thread_eq(lock->writer_thread, curr_thread)) {
/* Relock */ /* Relock */
lock->lock_word -= X_LOCK_DECR; lock->lock_word -= X_LOCK_DECR;
......
...@@ -438,6 +438,7 @@ rw_lock_x_lock_func_nowait( ...@@ -438,6 +438,7 @@ rw_lock_x_lock_func_nowait(
os_thread_id_t curr_thread = os_thread_get_curr_id(); os_thread_id_t curr_thread = os_thread_get_curr_id();
ibool success; ibool success;
ibool local_recursive= lock->recursive;
#ifdef INNODB_RW_LOCKS_USE_ATOMICS #ifdef INNODB_RW_LOCKS_USE_ATOMICS
success = os_compare_and_swap_lint(&lock->lock_word, X_LOCK_DECR, 0); success = os_compare_and_swap_lint(&lock->lock_word, X_LOCK_DECR, 0);
...@@ -452,10 +453,14 @@ rw_lock_x_lock_func_nowait( ...@@ -452,10 +453,14 @@ rw_lock_x_lock_func_nowait(
mutex_exit(&(lock->mutex)); mutex_exit(&(lock->mutex));
#endif #endif
/* Note: recursive must be loaded before writer_thread see
comment for rw_lock_set_writer_id_and_recursion_flag().
To achieve this we load it before os_compare_and_swap_lint(),
which implies full memory barrier in current implementation. */
if (success) { if (success) {
rw_lock_set_writer_id_and_recursion_flag(lock, TRUE); rw_lock_set_writer_id_and_recursion_flag(lock, TRUE);
} else if (lock->recursive } else if (local_recursive
&& os_thread_eq(lock->writer_thread, curr_thread)) { && os_thread_eq(lock->writer_thread, curr_thread)) {
/* Relock: this lock_word modification is safe since no other /* Relock: this lock_word modification is safe since no other
threads can modify (lock, unlock, or reserve) lock_word while threads can modify (lock, unlock, or reserve) lock_word while
......
...@@ -563,6 +563,7 @@ rw_lock_x_lock_low( ...@@ -563,6 +563,7 @@ rw_lock_x_lock_low(
ulint line) /*!< in: line where requested */ ulint line) /*!< in: line where requested */
{ {
os_thread_id_t curr_thread = os_thread_get_curr_id(); os_thread_id_t curr_thread = os_thread_get_curr_id();
ibool local_recursive= lock->recursive;
if (rw_lock_lock_word_decr(lock, X_LOCK_DECR)) { if (rw_lock_lock_word_decr(lock, X_LOCK_DECR)) {
...@@ -583,10 +584,12 @@ rw_lock_x_lock_low( ...@@ -583,10 +584,12 @@ rw_lock_x_lock_low(
file_name, line); file_name, line);
} else { } else {
if (!pass) /* Decrement failed: relock or failed lock
os_rmb; Note: recursive must be loaded before writer_thread see
/* Decrement failed: relock or failed lock */ comment for rw_lock_set_writer_id_and_recursion_flag().
if (!pass && lock->recursive To achieve this we load it before rw_lock_lock_word_decr(),
which implies full memory barrier in current implementation. */
if (!pass && local_recursive
&& os_thread_eq(lock->writer_thread, curr_thread)) { && os_thread_eq(lock->writer_thread, curr_thread)) {
/* Relock */ /* Relock */
lock->lock_word -= X_LOCK_DECR; lock->lock_word -= X_LOCK_DECR;
......
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