mirror of
https://github.com/MariaDB/server.git
synced 2025-08-09 22:24:09 +03:00
MDEV-8869: Potential lock_sys->mutex deadlock
In wsrep BF we have already took lock_sys and trx mutex either on wsrep_abort_transaction() or before wsrep_kill_victim(). In replication we could own lock_sys mutex taken in lock_deadlock_check_and_resolve().
This commit is contained in:
@@ -4358,29 +4358,46 @@ innobase_kill_query(
|
|||||||
#endif /* WITH_WSREP */
|
#endif /* WITH_WSREP */
|
||||||
trx = thd_to_trx(thd);
|
trx = thd_to_trx(thd);
|
||||||
|
|
||||||
if (trx) {
|
if (trx && trx->lock.wait_lock) {
|
||||||
/* In wsrep BF we have already took lock_sys and trx
|
/* In wsrep BF we have already took lock_sys and trx
|
||||||
mutex either on wsrep_abort_transaction() or
|
mutex either on wsrep_abort_transaction() or
|
||||||
before wsrep_kill_victim() */
|
before wsrep_kill_victim(). In replication we
|
||||||
|
could own lock_sys mutex taken in
|
||||||
|
lock_deadlock_check_and_resolve(). */
|
||||||
|
|
||||||
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
|
WSREP_DEBUG("Killing victim trx %p BF %d trx BF %d trx_id " TRX_ID_FMT " ABORT %d thd %p"
|
||||||
|
" current_thd %p BF %d wait_lock_modes: %s\n",
|
||||||
|
trx, wsrep_thd_is_BF(trx->mysql_thd, FALSE),
|
||||||
|
wsrep_thd_is_BF(thd, FALSE),
|
||||||
|
trx->id, trx->abort_type,
|
||||||
|
trx->mysql_thd,
|
||||||
|
current_thd,
|
||||||
|
wsrep_thd_is_BF(current_thd, FALSE),
|
||||||
|
lock_get_info(trx->lock.wait_lock).c_str());
|
||||||
|
|
||||||
|
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
|
||||||
|
trx->abort_type == TRX_SERVER_ABORT) {
|
||||||
ut_ad(!lock_mutex_own());
|
ut_ad(!lock_mutex_own());
|
||||||
ut_ad(!trx_mutex_own(trx));
|
|
||||||
lock_mutex_enter();
|
lock_mutex_enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trx->abort_type != TRX_WSREP_ABORT) {
|
||||||
trx_mutex_enter(trx);
|
trx_mutex_enter(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
ut_ad(lock_mutex_own());
|
ut_ad(lock_mutex_own());
|
||||||
ut_ad(trx_mutex_own(trx));
|
ut_ad(trx_mutex_own(trx));
|
||||||
|
|
||||||
/* We did dirty read above in case when abort is not
|
|
||||||
from wsrep. */
|
|
||||||
if (trx->lock.wait_lock) {
|
if (trx->lock.wait_lock) {
|
||||||
lock_cancel_waiting_and_release(trx->lock.wait_lock);
|
lock_cancel_waiting_and_release(trx->lock.wait_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
|
if (trx->abort_type != TRX_WSREP_ABORT) {
|
||||||
trx_mutex_exit(trx);
|
trx_mutex_exit(trx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
|
||||||
|
trx->abort_type == TRX_SERVER_ABORT) {
|
||||||
lock_mutex_exit();
|
lock_mutex_exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17537,12 +17554,12 @@ wsrep_abort_transaction(
|
|||||||
if (victim_trx) {
|
if (victim_trx) {
|
||||||
lock_mutex_enter();
|
lock_mutex_enter();
|
||||||
trx_mutex_enter(victim_trx);
|
trx_mutex_enter(victim_trx);
|
||||||
victim_trx->wsrep_abort = TRUE;
|
victim_trx->abort_type = TRX_WSREP_ABORT;
|
||||||
int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
|
int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
|
||||||
victim_trx, signal);
|
victim_trx, signal);
|
||||||
trx_mutex_exit(victim_trx);
|
trx_mutex_exit(victim_trx);
|
||||||
lock_mutex_exit();
|
lock_mutex_exit();
|
||||||
victim_trx->wsrep_abort = FALSE;
|
victim_trx->abort_type = TRX_SERVER_ABORT;
|
||||||
wsrep_srv_conc_cancel_wait(victim_trx);
|
wsrep_srv_conc_cancel_wait(victim_trx);
|
||||||
DBUG_RETURN(rcode);
|
DBUG_RETURN(rcode);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -991,6 +991,14 @@ void
|
|||||||
lock_cancel_waiting_and_release(
|
lock_cancel_waiting_and_release(
|
||||||
/*============================*/
|
/*============================*/
|
||||||
lock_t* lock); /*!< in/out: waiting lock request */
|
lock_t* lock); /*!< in/out: waiting lock request */
|
||||||
|
|
||||||
|
/*******************************************************************//**
|
||||||
|
Get lock mode and table/index name
|
||||||
|
@return string containing lock info */
|
||||||
|
std::string
|
||||||
|
lock_get_info(
|
||||||
|
const lock_t*);
|
||||||
|
|
||||||
#endif /* WITH_WSREP */
|
#endif /* WITH_WSREP */
|
||||||
#ifndef UNIV_NONINL
|
#ifndef UNIV_NONINL
|
||||||
#include "lock0lock.ic"
|
#include "lock0lock.ic"
|
||||||
|
@@ -673,6 +673,12 @@ lock_rec_convert_impl_to_expl()) will access transactions associated
|
|||||||
to other connections. The locks of transactions are protected by
|
to other connections. The locks of transactions are protected by
|
||||||
lock_sys->mutex and sometimes by trx->mutex. */
|
lock_sys->mutex and sometimes by trx->mutex. */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TRX_SERVER_ABORT = 0,
|
||||||
|
TRX_WSREP_ABORT = 1,
|
||||||
|
TRX_REPLICATION_ABORT = 2
|
||||||
|
} trx_abort_t;
|
||||||
|
|
||||||
struct trx_t{
|
struct trx_t{
|
||||||
ulint magic_n;
|
ulint magic_n;
|
||||||
|
|
||||||
@@ -848,8 +854,8 @@ struct trx_t{
|
|||||||
/*------------------------------*/
|
/*------------------------------*/
|
||||||
THD* mysql_thd; /*!< MySQL thread handle corresponding
|
THD* mysql_thd; /*!< MySQL thread handle corresponding
|
||||||
to this trx, or NULL */
|
to this trx, or NULL */
|
||||||
ibool wsrep_abort; /*!< Transaction is aborted from
|
trx_abort_t abort_type; /*!< Transaction abort type*/
|
||||||
wsrep functions. */
|
|
||||||
const char* mysql_log_file_name;
|
const char* mysql_log_file_name;
|
||||||
/*!< if MySQL binlog is used, this field
|
/*!< if MySQL binlog is used, this field
|
||||||
contains a pointer to the latest file
|
contains a pointer to the latest file
|
||||||
|
@@ -57,6 +57,10 @@ extern my_bool wsrep_debug;
|
|||||||
extern my_bool wsrep_log_conflicts;
|
extern my_bool wsrep_log_conflicts;
|
||||||
#include "ha_prototypes.h"
|
#include "ha_prototypes.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
/* Restricts the length of search we will do in the waits-for
|
/* Restricts the length of search we will do in the waits-for
|
||||||
graph of transactions */
|
graph of transactions */
|
||||||
#define LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000
|
#define LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000
|
||||||
@@ -1729,10 +1733,10 @@ wsrep_kill_victim(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lock->trx->wsrep_abort = TRUE;
|
lock->trx->abort_type = TRX_WSREP_ABORT;
|
||||||
wsrep_innobase_kill_one_trx(trx->mysql_thd,
|
wsrep_innobase_kill_one_trx(trx->mysql_thd,
|
||||||
(const trx_t*) trx, lock->trx, TRUE);
|
(const trx_t*) trx, lock->trx, TRUE);
|
||||||
lock->trx->wsrep_abort = FALSE;
|
lock->trx->abort_type = TRX_SERVER_ABORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4401,7 +4405,9 @@ lock_report_waiters_to_mysql(
|
|||||||
innobase_kill_query. We mark this by setting
|
innobase_kill_query. We mark this by setting
|
||||||
current_lock_mutex_owner, so we can avoid trying
|
current_lock_mutex_owner, so we can avoid trying
|
||||||
to recursively take lock_sys->mutex. */
|
to recursively take lock_sys->mutex. */
|
||||||
|
w_trx->abort_type = TRX_REPLICATION_ABORT;
|
||||||
thd_report_wait_for(mysql_thd, w_trx->mysql_thd);
|
thd_report_wait_for(mysql_thd, w_trx->mysql_thd);
|
||||||
|
w_trx->abort_type = TRX_SERVER_ABORT;
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
@@ -7741,3 +7747,32 @@ lock_trx_has_rec_x_lock(
|
|||||||
return(true);
|
return(true);
|
||||||
}
|
}
|
||||||
#endif /* UNIV_DEBUG */
|
#endif /* UNIV_DEBUG */
|
||||||
|
|
||||||
|
/*******************************************************************//**
|
||||||
|
Get lock mode and table/index name
|
||||||
|
@return string containing lock info */
|
||||||
|
std::string
|
||||||
|
lock_get_info(
|
||||||
|
const lock_t* lock)
|
||||||
|
{
|
||||||
|
std::string info;
|
||||||
|
std::string mode("mode ");
|
||||||
|
std::string index("index ");
|
||||||
|
std::string table("table ");
|
||||||
|
std::string n_uniq(" n_uniq");
|
||||||
|
std::string n_user(" n_user");
|
||||||
|
std::string lock_mode((lock_get_mode_str(lock)));
|
||||||
|
std::string iname(lock->index->name);
|
||||||
|
std::string tname(lock->index->table_name);
|
||||||
|
|
||||||
|
#define SSTR( x ) reinterpret_cast< std::ostringstream & >( \
|
||||||
|
( std::ostringstream() << std::dec << x ) ).str()
|
||||||
|
|
||||||
|
info = mode + lock_mode
|
||||||
|
+ index + iname
|
||||||
|
+ table + tname
|
||||||
|
+ n_uniq + SSTR(lock->index->n_uniq)
|
||||||
|
+ n_user + SSTR(lock->index->n_user_defined_cols);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
@@ -5039,25 +5039,46 @@ innobase_kill_connection(
|
|||||||
|
|
||||||
trx = thd_to_trx(thd);
|
trx = thd_to_trx(thd);
|
||||||
|
|
||||||
if (trx) {
|
if (trx && trx->lock.wait_lock) {
|
||||||
/* In wsrep BF we have already took lock_sys and trx
|
/* In wsrep BF we have already took lock_sys and trx
|
||||||
mutex either on wsrep_abort_transaction() or
|
mutex either on wsrep_abort_transaction() or
|
||||||
before wsrep_kill_victim() */
|
before wsrep_kill_victim(). In replication we
|
||||||
|
could own lock_sys mutex taken in
|
||||||
|
lock_deadlock_check_and_resolve().*/
|
||||||
|
|
||||||
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
|
WSREP_DEBUG("Killing victim trx %p BF %d trx BF %d trx_id " TRX_ID_FMT " ABORT %d thd %p"
|
||||||
|
" current_thd %p BF %d wait_lock_modes: %s\n",
|
||||||
|
trx, wsrep_thd_is_BF(trx->mysql_thd, FALSE),
|
||||||
|
wsrep_thd_is_BF(thd, FALSE),
|
||||||
|
trx->id, trx->abort_type,
|
||||||
|
trx->mysql_thd,
|
||||||
|
current_thd,
|
||||||
|
wsrep_thd_is_BF(current_thd, FALSE),
|
||||||
|
lock_get_info(trx->lock.wait_lock).c_str());
|
||||||
|
|
||||||
|
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
|
||||||
|
trx->abort_type == TRX_SERVER_ABORT) {
|
||||||
ut_ad(!lock_mutex_own());
|
ut_ad(!lock_mutex_own());
|
||||||
ut_ad(!trx_mutex_own(trx));
|
|
||||||
lock_mutex_enter();
|
lock_mutex_enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trx->abort_type != TRX_WSREP_ABORT) {
|
||||||
trx_mutex_enter(trx);
|
trx_mutex_enter(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
ut_ad(lock_mutex_own());
|
ut_ad(lock_mutex_own());
|
||||||
ut_ad(trx_mutex_own(trx));
|
ut_ad(trx_mutex_own(trx));
|
||||||
|
|
||||||
lock_cancel_waiting_and_release(trx->lock.wait_lock);
|
if (trx->lock.wait_lock) {
|
||||||
|
lock_cancel_waiting_and_release(trx->lock.wait_lock);
|
||||||
|
}
|
||||||
|
|
||||||
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
|
if (trx->abort_type != TRX_WSREP_ABORT) {
|
||||||
trx_mutex_exit(trx);
|
trx_mutex_exit(trx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) &&
|
||||||
|
trx->abort_type == TRX_SERVER_ABORT) {
|
||||||
lock_mutex_exit();
|
lock_mutex_exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18699,12 +18720,12 @@ wsrep_abort_transaction(handlerton* hton, THD *bf_thd, THD *victim_thd,
|
|||||||
if (victim_trx) {
|
if (victim_trx) {
|
||||||
lock_mutex_enter();
|
lock_mutex_enter();
|
||||||
trx_mutex_enter(victim_trx);
|
trx_mutex_enter(victim_trx);
|
||||||
victim_trx->wsrep_abort = TRUE;
|
victim_trx->abort_type = TRX_WSREP_ABORT;
|
||||||
int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
|
int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
|
||||||
victim_trx, signal);
|
victim_trx, signal);
|
||||||
trx_mutex_exit(victim_trx);
|
trx_mutex_exit(victim_trx);
|
||||||
lock_mutex_exit();
|
lock_mutex_exit();
|
||||||
victim_trx->wsrep_abort = FALSE;
|
victim_trx->abort_type = TRX_SERVER_ABORT;
|
||||||
wsrep_srv_conc_cancel_wait(victim_trx);
|
wsrep_srv_conc_cancel_wait(victim_trx);
|
||||||
DBUG_RETURN(rcode);
|
DBUG_RETURN(rcode);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -39,6 +39,8 @@ Created 5/7/1996 Heikki Tuuri
|
|||||||
#include "srv0srv.h"
|
#include "srv0srv.h"
|
||||||
#include "ut0vec.h"
|
#include "ut0vec.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#ifdef UNIV_DEBUG
|
#ifdef UNIV_DEBUG
|
||||||
extern ibool lock_print_waits;
|
extern ibool lock_print_waits;
|
||||||
#endif /* UNIV_DEBUG */
|
#endif /* UNIV_DEBUG */
|
||||||
@@ -995,6 +997,13 @@ extern lock_sys_t* lock_sys;
|
|||||||
mutex_exit(&lock_sys->wait_mutex); \
|
mutex_exit(&lock_sys->wait_mutex); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/*******************************************************************//**
|
||||||
|
Get lock mode and table/index name
|
||||||
|
@return string containing lock info */
|
||||||
|
std::string
|
||||||
|
lock_get_info(
|
||||||
|
const lock_t*);
|
||||||
|
|
||||||
#ifndef UNIV_NONINL
|
#ifndef UNIV_NONINL
|
||||||
#include "lock0lock.ic"
|
#include "lock0lock.ic"
|
||||||
#endif
|
#endif
|
||||||
|
@@ -705,6 +705,12 @@ lock_rec_convert_impl_to_expl()) will access transactions associated
|
|||||||
to other connections. The locks of transactions are protected by
|
to other connections. The locks of transactions are protected by
|
||||||
lock_sys->mutex and sometimes by trx->mutex. */
|
lock_sys->mutex and sometimes by trx->mutex. */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TRX_SERVER_ABORT = 0,
|
||||||
|
TRX_WSREP_ABORT = 1,
|
||||||
|
TRX_REPLICATION_ABORT = 2
|
||||||
|
} trx_abort_t;
|
||||||
|
|
||||||
struct trx_t{
|
struct trx_t{
|
||||||
ulint magic_n;
|
ulint magic_n;
|
||||||
|
|
||||||
@@ -881,8 +887,8 @@ struct trx_t{
|
|||||||
/*------------------------------*/
|
/*------------------------------*/
|
||||||
THD* mysql_thd; /*!< MySQL thread handle corresponding
|
THD* mysql_thd; /*!< MySQL thread handle corresponding
|
||||||
to this trx, or NULL */
|
to this trx, or NULL */
|
||||||
ibool wsrep_abort; /*!< Transaction is aborted from
|
trx_abort_t abort_type; /*!< Transaction abort type */
|
||||||
wsrep functions. */
|
|
||||||
const char* mysql_log_file_name;
|
const char* mysql_log_file_name;
|
||||||
/*!< if MySQL binlog is used, this field
|
/*!< if MySQL binlog is used, this field
|
||||||
contains a pointer to the latest file
|
contains a pointer to the latest file
|
||||||
|
@@ -57,6 +57,10 @@ extern my_bool wsrep_debug;
|
|||||||
extern my_bool wsrep_log_conflicts;
|
extern my_bool wsrep_log_conflicts;
|
||||||
#include "ha_prototypes.h"
|
#include "ha_prototypes.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
/* Restricts the length of search we will do in the waits-for
|
/* Restricts the length of search we will do in the waits-for
|
||||||
graph of transactions */
|
graph of transactions */
|
||||||
#define LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000
|
#define LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000
|
||||||
@@ -1728,10 +1732,10 @@ wsrep_kill_victim(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lock->trx->wsrep_abort = TRUE;
|
lock->trx->abort_type = TRX_WSREP_ABORT;
|
||||||
wsrep_innobase_kill_one_trx(trx->mysql_thd,
|
wsrep_innobase_kill_one_trx(trx->mysql_thd,
|
||||||
(const trx_t*) trx, lock->trx, TRUE);
|
(const trx_t*) trx, lock->trx, TRUE);
|
||||||
lock->trx->wsrep_abort = FALSE;
|
lock->trx->abort_type = TRX_SERVER_ABORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4426,7 +4430,9 @@ lock_report_waiters_to_mysql(
|
|||||||
innobase_kill_query. We mark this by setting
|
innobase_kill_query. We mark this by setting
|
||||||
current_lock_mutex_owner, so we can avoid trying
|
current_lock_mutex_owner, so we can avoid trying
|
||||||
to recursively take lock_sys->mutex. */
|
to recursively take lock_sys->mutex. */
|
||||||
|
w_trx->abort_type = TRX_REPLICATION_ABORT;
|
||||||
thd_report_wait_for(mysql_thd, w_trx->mysql_thd);
|
thd_report_wait_for(mysql_thd, w_trx->mysql_thd);
|
||||||
|
w_trx->abort_type = TRX_SERVER_ABORT;
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
@@ -7852,3 +7858,32 @@ lock_trx_has_rec_x_lock(
|
|||||||
return(true);
|
return(true);
|
||||||
}
|
}
|
||||||
#endif /* UNIV_DEBUG */
|
#endif /* UNIV_DEBUG */
|
||||||
|
|
||||||
|
/*******************************************************************//**
|
||||||
|
Get lock mode and table/index name
|
||||||
|
@return string containing lock info */
|
||||||
|
std::string
|
||||||
|
lock_get_info(
|
||||||
|
const lock_t* lock)
|
||||||
|
{
|
||||||
|
std::string info;
|
||||||
|
std::string mode("mode ");
|
||||||
|
std::string index("index ");
|
||||||
|
std::string table("table ");
|
||||||
|
std::string n_uniq(" n_uniq");
|
||||||
|
std::string n_user(" n_user");
|
||||||
|
std::string lock_mode((lock_get_mode_str(lock)));
|
||||||
|
std::string iname(lock->index->name);
|
||||||
|
std::string tname(lock->index->table_name);
|
||||||
|
|
||||||
|
#define SSTR( x ) reinterpret_cast< std::ostringstream & >( \
|
||||||
|
( std::ostringstream() << std::dec << x ) ).str()
|
||||||
|
|
||||||
|
info = mode + lock_mode
|
||||||
|
+ index + iname
|
||||||
|
+ table + tname
|
||||||
|
+ n_uniq + SSTR(lock->index->n_uniq)
|
||||||
|
+ n_user + SSTR(lock->index->n_user_defined_cols);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user