------------------------------------------------------------ revno: 8782 committer: Laurynas Biveinis branch nick: mysql-5.7-percona-patches timestamp: Fri 2015-01-23 11:20:59 +0200 message: Fix bug http://bugs.mysql.com/bug.php?id=75595. Implement new option innodb-log-checksum-algorithm with same possible values and algorithms as innodb-checksum-algorithm. Patch author Alexey Stroganov. diff: === added file 'mysql-test/suite/sys_vars/r/innodb_log_checksum_algorithm_basic.result' --- mysql-test/suite/sys_vars/r/innodb_log_checksum_algorithm_basic.result 1970-01-01 00:00:00 +0000 +++ mysql-test/suite/sys_vars/r/innodb_log_checksum_algorithm_basic.result 2015-01-23 09:20:59 +0000 @@ -0,0 +1,47 @@ +SET @orig = @@global.innodb_log_checksum_algorithm; +SELECT @orig; +@orig +innodb +SET GLOBAL innodb_log_checksum_algorithm = 'crc32'; +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +crc32 +SET GLOBAL innodb_log_checksum_algorithm = 'strict_crc32'; +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +strict_crc32 +SET GLOBAL innodb_log_checksum_algorithm = 'innodb'; +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +innodb +SET GLOBAL innodb_log_checksum_algorithm = 'strict_innodb'; +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +strict_innodb +SET GLOBAL innodb_log_checksum_algorithm = 'none'; +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +none +SET GLOBAL innodb_log_checksum_algorithm = 'strict_none'; +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +strict_none +SET GLOBAL innodb_log_checksum_algorithm = ''; +ERROR 42000: Variable 'innodb_log_checksum_algorithm' can't be set to the value of '' +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +strict_none +SET GLOBAL innodb_log_checksum_algorithm = 'foobar'; +ERROR 42000: Variable 'innodb_log_checksum_algorithm' can't be set to the value of 'foobar' +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +strict_none +SET GLOBAL innodb_log_checksum_algorithm = 123; +ERROR 42000: Variable 'innodb_log_checksum_algorithm' can't be set to the value of '123' +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +strict_none +SET GLOBAL innodb_log_checksum_algorithm = @orig; +SELECT @@global.innodb_log_checksum_algorithm; +@@global.innodb_log_checksum_algorithm +innodb === added file 'mysql-test/suite/sys_vars/t/innodb_log_checksum_algorithm_basic.test' --- mysql-test/suite/sys_vars/t/innodb_log_checksum_algorithm_basic.test 1970-01-01 00:00:00 +0000 +++ mysql-test/suite/sys_vars/t/innodb_log_checksum_algorithm_basic.test 2015-01-23 09:20:59 +0000 @@ -0,0 +1,38 @@ +--source include/have_innodb.inc + +# Check the default value +SET @orig = @@global.innodb_log_checksum_algorithm; +SELECT @orig; + +SET GLOBAL innodb_log_checksum_algorithm = 'crc32'; +SELECT @@global.innodb_log_checksum_algorithm; + +SET GLOBAL innodb_log_checksum_algorithm = 'strict_crc32'; +SELECT @@global.innodb_log_checksum_algorithm; + +SET GLOBAL innodb_log_checksum_algorithm = 'innodb'; +SELECT @@global.innodb_log_checksum_algorithm; + +SET GLOBAL innodb_log_checksum_algorithm = 'strict_innodb'; +SELECT @@global.innodb_log_checksum_algorithm; + +SET GLOBAL innodb_log_checksum_algorithm = 'none'; +SELECT @@global.innodb_log_checksum_algorithm; + +SET GLOBAL innodb_log_checksum_algorithm = 'strict_none'; +SELECT @@global.innodb_log_checksum_algorithm; + +-- error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL innodb_log_checksum_algorithm = ''; +SELECT @@global.innodb_log_checksum_algorithm; + +-- error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL innodb_log_checksum_algorithm = 'foobar'; +SELECT @@global.innodb_log_checksum_algorithm; + +-- error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL innodb_log_checksum_algorithm = 123; +SELECT @@global.innodb_log_checksum_algorithm; + +SET GLOBAL innodb_log_checksum_algorithm = @orig; +SELECT @@global.innodb_log_checksum_algorithm; === modified file 'storage/innobase/handler/ha_innodb.cc' --- storage/innobase/handler/ha_innodb.cc 2015-01-16 19:30:41 +0000 +++ storage/innobase/handler/ha_innodb.cc 2015-01-23 09:20:59 +0000 @@ -201,7 +201,8 @@ NULL }; -/** Possible values for system variable "innodb_checksum_algorithm". */ +/** Possible values for system variable "innodb_checksum_algorithm" and +"innodb_log_checksum_algorithm". */ static const char* innodb_checksum_algorithm_names[] = { "crc32", "strict_crc32", @@ -2986,6 +2987,29 @@ return false; } +/** Update log_checksum_algorithm_ptr with a pointer to the function +corresponding to the given checksum algorithm. +@param[in] algorithm algorithm */ +static +void +innodb_log_checksum_func_update( + ulint algorithm) +{ + switch (algorithm) { + case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB: + case SRV_CHECKSUM_ALGORITHM_INNODB: + log_checksum_algorithm_ptr = log_block_calc_checksum_innodb; + break; + case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32: + case SRV_CHECKSUM_ALGORITHM_CRC32: + log_checksum_algorithm_ptr = log_block_calc_checksum_crc32; + break; + case SRV_CHECKSUM_ALGORITHM_STRICT_NONE: + case SRV_CHECKSUM_ALGORITHM_NONE: + log_checksum_algorithm_ptr = log_block_calc_checksum_none; + break; + } +} /*********************************************************************//** Opens an InnoDB database. @@ -3346,6 +3370,8 @@ srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_NONE; } + innodb_log_checksum_func_update(srv_log_checksum_algorithm); + #ifdef HAVE_LINUX_LARGE_PAGES if ((os_use_large_pages = my_use_large_pages)) { os_large_page_size = opt_large_page_size; @@ -16334,6 +16360,35 @@ os_event_set(lock_sys->timeout_event); } +/** On update hook for the innodb_log_checksum_algorithm variable. +@param[in] thd thread handle +@param[in] var system variable +@param[out] var_ptr current value +@param[in] save immediate result from check function */ +static +void +innodb_log_checksum_algorithm_update( +/*=================================*/ + THD* thd, + struct st_mysql_sys_var* var, + void* var_ptr, + const void* save) +{ + srv_checksum_algorithm_t algorithm; + + algorithm = static_cast + (*static_cast(save)); + + /* Make sure we are the only log user */ + mutex_enter(&log_sys->mutex); + + innodb_log_checksum_func_update(algorithm); + + srv_log_checksum_algorithm = algorithm; + + mutex_exit(&log_sys->mutex); +} + static SHOW_VAR innodb_status_variables_export[]= { {"Innodb", (char*) &show_innodb_vars, SHOW_FUNC}, {NullS, NullS, SHOW_LONG} @@ -16368,6 +16423,30 @@ NULL, NULL, SRV_CHECKSUM_ALGORITHM_INNODB, &innodb_checksum_algorithm_typelib); +static MYSQL_SYSVAR_ENUM(log_checksum_algorithm, srv_log_checksum_algorithm, + PLUGIN_VAR_RQCMDARG, + "The algorithm InnoDB uses for redo log block checksums. Possible values are" + " CRC32 (hardware accelerated if the CPU supports it)" + " write crc32, allow any of the other checksums to match when reading;" + " STRICT_CRC32 " + " write crc32, do not allow other algorithms to match when reading;" + " INNODB" + " write a software calculated checksum, allow any other checksums" + " to match when reading;" + " STRICT_INNODB" + " write a software calculated checksum, do not allow other algorithms" + " to match when reading;" + " NONE" + " write a constant magic number, do not do any checksum verification" + " when reading" + " STRICT_NONE" + " write a constant magic number, do not allow values other than that" + " magic number when reading;" + " Redo logs created when this option is set to crc32, strict_crc32, none, or" + " strict_none will not be readable by MySQL versions older than 5.7.6", + NULL, innodb_log_checksum_algorithm_update, SRV_CHECKSUM_ALGORITHM_INNODB, + &innodb_checksum_algorithm_typelib); + static MYSQL_SYSVAR_BOOL(checksums, innobase_use_checksums, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "DEPRECATED. Use innodb_checksum_algorithm=NONE instead of setting" @@ -17205,6 +17284,7 @@ MYSQL_SYSVAR(lru_scan_depth), MYSQL_SYSVAR(flush_neighbors), MYSQL_SYSVAR(checksum_algorithm), + MYSQL_SYSVAR(log_checksum_algorithm), MYSQL_SYSVAR(checksums), MYSQL_SYSVAR(commit_concurrency), MYSQL_SYSVAR(concurrency_tickets), === modified file 'storage/innobase/include/log0log.h' --- storage/innobase/include/log0log.h 2014-08-11 07:43:11 +0000 +++ storage/innobase/include/log0log.h 2015-01-23 09:20:59 +0000 @@ -53,6 +53,15 @@ /** Redo log group */ struct log_group_t; +/** Magic value to use instead of log checksums when they are disabled */ +#define LOG_NO_CHECKSUM_MAGIC 0xDEADBEEFUL + +typedef ulint (*log_checksum_func_t)(const byte* log_block); + +/** Pointer to the log checksum calculation function. Protected with +log_sys->mutex. */ +extern log_checksum_func_t log_checksum_algorithm_ptr; + /** Maximum number of log groups in log_group_t::checkpoint_buf */ #define LOG_MAX_N_GROUPS 32 @@ -366,7 +375,37 @@ ulint log_block_calc_checksum( /*====================*/ - const byte* block); /*!< in: log block */ + const byte* block) /*!< in: log block */ + __attribute__((pure, warn_unused_result)); + +/** Calculates the checksum for a log block using the legacy InnoDB algorithm. +@param[in] block log block +@return checksum */ +UNIV_INLINE +ulint +log_block_calc_checksum_innodb( +/*===========================*/ + const byte* block) + __attribute__((pure, warn_unused_result)); + +/** Calculates the checksum for a log block using the CRC32 algorithm. +@param[in] block log block +@return checksum */ +UNIV_INLINE +ulint +log_block_calc_checksum_crc32( + const byte* block) + __attribute__((pure, warn_unused_result)); + +/** Calculates the checksum for a log block using the "no-op" algorithm. +@param[in] block log block +@return checksum */ +UNIV_INLINE +ulint +log_block_calc_checksum_none( + const byte* block) + __attribute__((const, warn_unused_result)); + /************************************************************//** Gets a log block checksum field value. @return checksum */ === modified file 'storage/innobase/include/log0log.ic' --- storage/innobase/include/log0log.ic 2014-05-07 14:07:54 +0000 +++ storage/innobase/include/log0log.ic 2015-01-23 09:20:59 +0000 @@ -26,6 +26,8 @@ #include "os0file.h" #include "mach0data.h" #include "srv0mon.h" +#include "srv0srv.h" /* log_checksum_algorithm_ptr */ +#include "ut0crc32.h" /* ut_crc32 */ #ifdef UNIV_LOG_LSN_DEBUG #include "mtr0types.h" @@ -187,7 +189,7 @@ } /************************************************************//** -Calculates the checksum for a log block. +Calculates the checksum for a log block using the current algorithm. @return checksum */ UNIV_INLINE ulint @@ -195,6 +197,17 @@ /*====================*/ const byte* block) /*!< in: log block */ { + return(log_checksum_algorithm_ptr(block)); +} + +/** Calculates the checksum for a log block using the legacy InnoDB algorithm. +@param[in] block log block +@return checksum */ +UNIV_INLINE +ulint +log_block_calc_checksum_innodb( + const byte* block) +{ ulint sum; ulint sh; ulint i; @@ -216,6 +229,28 @@ return(sum); } +/** Calculates the checksum for a log block using the CRC32 algorithm. +@param[in] block log block +@return checksum */ +UNIV_INLINE +ulint +log_block_calc_checksum_crc32( + const byte* block) +{ + return(ut_crc32(block, OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE)); +} + +/** Calculates the checksum for a log block using the "no-op" algorithm. +@param[in] block log block +@return checksum */ +UNIV_INLINE +ulint +log_block_calc_checksum_none( + const byte* block) +{ + return(LOG_NO_CHECKSUM_MAGIC); +} + /************************************************************//** Gets a log block checksum field value. @return checksum */ === modified file 'storage/innobase/include/srv0srv.h' --- storage/innobase/include/srv0srv.h 2015-01-16 19:30:41 +0000 +++ storage/innobase/include/srv0srv.h 2015-01-23 09:20:59 +0000 @@ -361,6 +361,8 @@ extern ulong srv_doublewrite_batch_size; extern ulong srv_checksum_algorithm; +extern ulong srv_log_checksum_algorithm; + extern double srv_max_buf_pool_modified_pct; extern ulong srv_max_purge_lag; extern ulong srv_max_purge_lag_delay; === modified file 'storage/innobase/log/log0log.cc' --- storage/innobase/log/log0log.cc 2014-08-20 12:04:07 +0000 +++ storage/innobase/log/log0log.cc 2015-01-23 09:20:59 +0000 @@ -82,6 +82,10 @@ /* Global log system variable */ log_t* log_sys = NULL; +/** Pointer to the log checksum calculation function */ +log_checksum_func_t log_checksum_algorithm_ptr = + log_block_calc_checksum_innodb; + /* These control how often we print warnings if the last checkpoint is too old */ ibool log_has_printed_chkp_warning = FALSE; === modified file 'storage/innobase/log/log0recv.cc' --- storage/innobase/log/log0recv.cc 2014-08-20 04:14:41 +0000 +++ storage/innobase/log/log0recv.cc 2015-01-23 09:20:59 +0000 @@ -963,12 +963,66 @@ /*===================================*/ const byte* block) /*!< in: pointer to a log block */ { - if (log_block_calc_checksum(block) == log_block_get_checksum(block)) { - - return(TRUE); - } - - if (log_block_get_hdr_no(block) == log_block_get_checksum(block)) { + ulint block_checksum = log_block_get_checksum(block); + + if (UNIV_LIKELY(log_block_calc_checksum(block) == block_checksum) || + srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_NONE) { + + return(TRUE); + } + + if (srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32 || + srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB || + srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_NONE) { + + const char* algo = NULL; + + ib::error() << "log block checksum mismatch: expected " + << block_checksum << ", calculated checksum " + << log_block_calc_checksum(block); + + if (block_checksum == LOG_NO_CHECKSUM_MAGIC) { + + algo = "none"; + } else if (block_checksum == + log_block_calc_checksum_crc32(block)) { + + algo = "crc32"; + } else if (block_checksum == + log_block_calc_checksum_innodb(block)) { + + algo = "innodb"; + } + + if (algo) { + + const char* current_algo; + + current_algo = buf_checksum_algorithm_name( + static_cast + (srv_log_checksum_algorithm)); + + ib::error() << "current InnoDB log checksum type: " + << current_algo + << ", detected log checksum type: " + << algo; + } + + ib::fatal() << "STRICT method was specified for " + "innodb_log_checksum, so we intentionally assert here."; + } + + ut_ad(srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_CRC32 || + srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_INNODB); + + if (block_checksum == log_block_calc_checksum_none(block) || + block_checksum == log_block_calc_checksum_crc32(block) || + block_checksum == log_block_calc_checksum_innodb(block)) { + + return(TRUE); + } + + if (log_block_get_hdr_no(block) == block_checksum) { /* We assume the log block is in the format of InnoDB version < 3.23.52 and the block is ok */ === modified file 'storage/innobase/srv/srv0srv.cc' --- storage/innobase/srv/srv0srv.cc 2015-01-16 19:30:41 +0000 +++ storage/innobase/srv/srv0srv.cc 2015-01-23 09:20:59 +0000 @@ -341,6 +341,8 @@ ulong srv_replication_delay = 0; +ulong srv_log_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_INNODB; + /*-------------------------------------------*/ ulong srv_n_spin_wait_rounds = 30; ulong srv_spin_wait_delay = 6;