diff options
Diffstat (limited to 'storage/xtradb/fil/fil0crypt.cc')
-rw-r--r-- | storage/xtradb/fil/fil0crypt.cc | 2662 |
1 files changed, 0 insertions, 2662 deletions
diff --git a/storage/xtradb/fil/fil0crypt.cc b/storage/xtradb/fil/fil0crypt.cc deleted file mode 100644 index e73d600d2ca..00000000000 --- a/storage/xtradb/fil/fil0crypt.cc +++ /dev/null @@ -1,2662 +0,0 @@ -/***************************************************************************** -Copyright (C) 2013, 2015, Google Inc. All Rights Reserved. -Copyright (c) 2014, 2017, MariaDB Corporation. All Rights Reserved. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., -51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*****************************************************************************/ -/**************************************************//** -@file fil0crypt.cc -Innodb file space encrypt/decrypt - -Created Jonas Oreland Google -Modified Jan Lindström jan.lindstrom@mariadb.com -*******************************************************/ - -#include "fil0fil.h" -#include "fil0crypt.h" -#include "srv0srv.h" -#include "srv0start.h" -#include "mach0data.h" -#include "log0recv.h" -#include "mtr0mtr.h" -#include "mtr0log.h" -#include "page0zip.h" -#include "ut0ut.h" -#include "btr0scrub.h" -#include "fsp0fsp.h" -#include "fil0pagecompress.h" -#include "ha_prototypes.h" // IB_LOG_ -#include <my_crypt.h> - -/** Mutex for keys */ -static ib_mutex_t fil_crypt_key_mutex; - -static bool fil_crypt_threads_inited = false; - -#ifdef UNIV_PFS_MUTEX -static mysql_pfs_key_t fil_crypt_key_mutex_key; -#endif - -/** Is encryption enabled/disabled */ -UNIV_INTERN ulong srv_encrypt_tables = 0; - -/** No of key rotation threads requested */ -UNIV_INTERN uint srv_n_fil_crypt_threads = 0; - -/** No of key rotation threads started */ -UNIV_INTERN uint srv_n_fil_crypt_threads_started = 0; - -/** At this age or older a space/page will be rotated */ -UNIV_INTERN uint srv_fil_crypt_rotate_key_age; - -/** Event to signal FROM the key rotation threads. */ -static os_event_t fil_crypt_event; - -/** Event to signal TO the key rotation threads. */ -UNIV_INTERN os_event_t fil_crypt_threads_event; - -/** Event for waking up threads throttle. */ -static os_event_t fil_crypt_throttle_sleep_event; - -/** Mutex for key rotation threads. */ -UNIV_INTERN ib_mutex_t fil_crypt_threads_mutex; - -#ifdef UNIV_PFS_MUTEX -static mysql_pfs_key_t fil_crypt_threads_mutex_key; -#endif - -/** Variable ensuring only 1 thread at time does initial conversion */ -static bool fil_crypt_start_converting = false; - -/** Variables for throttling */ -UNIV_INTERN uint srv_n_fil_crypt_iops = 100; // 10ms per iop -static uint srv_alloc_time = 3; // allocate iops for 3s at a time -static uint n_fil_crypt_iops_allocated = 0; - -/** Variables for scrubbing */ -extern uint srv_background_scrub_data_interval; -extern uint srv_background_scrub_data_check_interval; - -#define DEBUG_KEYROTATION_THROTTLING 0 - -/** Statistics variables */ -static fil_crypt_stat_t crypt_stat; -static ib_mutex_t crypt_stat_mutex; - -#ifdef UNIV_PFS_MUTEX -static mysql_pfs_key_t fil_crypt_stat_mutex_key; - -/** - * key for crypt data mutex -*/ -UNIV_INTERN mysql_pfs_key_t fil_crypt_data_mutex_key; -#endif - -/** Is background scrubbing enabled, defined on btr0scrub.cc */ -extern my_bool srv_background_scrub_data_uncompressed; -extern my_bool srv_background_scrub_data_compressed; - -static bool -fil_crypt_needs_rotation( - fil_encryption_t encrypt_mode, /*!< in: Encryption - mode */ - uint key_version, /*!< in: Key version */ - uint latest_key_version, /*!< in: Latest key version */ - uint rotate_key_age); /*!< in: When to rotate */ - -/********************************************************************* -Init space crypt */ -UNIV_INTERN -void -fil_space_crypt_init() -{ - mutex_create(fil_crypt_key_mutex_key, - &fil_crypt_key_mutex, SYNC_NO_ORDER_CHECK); - - fil_crypt_throttle_sleep_event = os_event_create(); - - mutex_create(fil_crypt_stat_mutex_key, - &crypt_stat_mutex, SYNC_NO_ORDER_CHECK); - - memset(&crypt_stat, 0, sizeof(crypt_stat)); -} - -/********************************************************************* -Cleanup space crypt */ -UNIV_INTERN -void -fil_space_crypt_cleanup() -{ - os_event_free(fil_crypt_throttle_sleep_event); - fil_crypt_throttle_sleep_event = NULL; - mutex_free(&fil_crypt_key_mutex); - mutex_free(&crypt_stat_mutex); -} - -/** -Get latest key version from encryption plugin. -@return key version or ENCRYPTION_KEY_VERSION_INVALID */ -uint -fil_space_crypt_t::key_get_latest_version(void) -{ - uint key_version = key_found; - - if (is_key_found()) { - key_version = encryption_key_get_latest_version(key_id); - srv_stats.n_key_requests.inc(); - key_found = key_version; - } - - return key_version; -} - -/****************************************************************** -Get the latest(key-version), waking the encrypt thread, if needed -@param[in,out] crypt_data Crypt data */ -static inline -uint -fil_crypt_get_latest_key_version( - fil_space_crypt_t* crypt_data) -{ - ut_ad(crypt_data != NULL); - - uint key_version = crypt_data->key_get_latest_version(); - - if (crypt_data->is_key_found()) { - - if (fil_crypt_needs_rotation(crypt_data->encryption, - crypt_data->min_key_version, - key_version, - srv_fil_crypt_rotate_key_age)) { - os_event_set(fil_crypt_threads_event); - } - } - - return key_version; -} - -/****************************************************************** -Mutex helper for crypt_data->scheme */ -void -crypt_data_scheme_locker( -/*=====================*/ - st_encryption_scheme* scheme, - int exit) -{ - fil_space_crypt_t* crypt_data = - static_cast<fil_space_crypt_t*>(scheme); - - if (exit) { - mutex_exit(&crypt_data->mutex); - } else { - mutex_enter(&crypt_data->mutex); - } -} - -/****************************************************************** -Create a fil_space_crypt_t object -@param[in] type CRYPT_SCHEME_UNENCRYPTE or - CRYPT_SCHEME_1 -@param[in] encrypt_mode FIL_ENCRYPTION_DEFAULT or - FIL_ENCRYPTION_ON or - FIL_ENCRYPTION_OFF -@param[in] min_key_version key_version or 0 -@param[in] key_id Used key id -@return crypt object */ -static -fil_space_crypt_t* -fil_space_create_crypt_data( - uint type, - fil_encryption_t encrypt_mode, - uint min_key_version, - uint key_id) -{ - void* buf = mem_zalloc(sizeof(fil_space_crypt_t)); - fil_space_crypt_t* crypt_data = NULL; - - if (buf) { - crypt_data = new(buf) - fil_space_crypt_t( - type, - min_key_version, - key_id, - encrypt_mode); - } - - return crypt_data; -} - -/****************************************************************** -Create a fil_space_crypt_t object -@param[in] encrypt_mode FIL_ENCRYPTION_DEFAULT or - FIL_ENCRYPTION_ON or - FIL_ENCRYPTION_OFF - -@param[in] key_id Encryption key id -@return crypt object */ -UNIV_INTERN -fil_space_crypt_t* -fil_space_create_crypt_data( - fil_encryption_t encrypt_mode, - uint key_id) -{ - return (fil_space_create_crypt_data(0, encrypt_mode, 0, key_id)); -} - -/****************************************************************** -Merge fil_space_crypt_t object -@param[in,out] dst Destination cryp data -@param[in] src Source crypt data */ -UNIV_INTERN -void -fil_space_merge_crypt_data( - fil_space_crypt_t* dst, - const fil_space_crypt_t* src) -{ - mutex_enter(&dst->mutex); - - /* validate that they are mergeable */ - ut_a(src->type == CRYPT_SCHEME_UNENCRYPTED || - src->type == CRYPT_SCHEME_1); - - ut_a(dst->type == CRYPT_SCHEME_UNENCRYPTED || - dst->type == CRYPT_SCHEME_1); - - dst->encryption = src->encryption; - dst->type = src->type; - dst->min_key_version = src->min_key_version; - dst->keyserver_requests += src->keyserver_requests; - - mutex_exit(&dst->mutex); -} - -/****************************************************************** -Read crypt data from a page (0) -@param[in] space space_id -@param[in] page Page 0 -@param[in] offset Offset to crypt data -@return crypt data from page 0 or NULL. */ -UNIV_INTERN -fil_space_crypt_t* -fil_space_read_crypt_data( - ulint space, - const byte* page, - ulint offset) -{ - if (memcmp(page + offset, CRYPT_MAGIC, MAGIC_SZ) != 0) { - /* Crypt data is not stored. */ - return NULL; - } - - ulint type = mach_read_from_1(page + offset + MAGIC_SZ + 0); - - if (! (type == CRYPT_SCHEME_UNENCRYPTED || - type == CRYPT_SCHEME_1)) { - - ib_logf(IB_LOG_LEVEL_ERROR, - "Found non sensible crypt scheme: " ULINTPF " for space " ULINTPF - " offset: " ULINTPF " bytes: " - "[ %.2x %.2x %.2x %.2x %.2x %.2x ].", - type, space, offset, - page[offset + 0 + MAGIC_SZ], - page[offset + 1 + MAGIC_SZ], - page[offset + 2 + MAGIC_SZ], - page[offset + 3 + MAGIC_SZ], - page[offset + 4 + MAGIC_SZ], - page[offset + 5 + MAGIC_SZ]); - ut_error; - } - - fil_space_crypt_t* crypt_data; - ulint iv_length = mach_read_from_1(page + offset + MAGIC_SZ + 1); - - if (! (iv_length == sizeof(crypt_data->iv))) { - ib_logf(IB_LOG_LEVEL_ERROR, - "Found non sensible iv length: %lu for space %lu " - " offset: %lu type: %lu bytes: " - "[ %.2x %.2x %.2x %.2x %.2x %.2x ].", - iv_length, space, offset, type, - page[offset + 0 + MAGIC_SZ], - page[offset + 1 + MAGIC_SZ], - page[offset + 2 + MAGIC_SZ], - page[offset + 3 + MAGIC_SZ], - page[offset + 4 + MAGIC_SZ], - page[offset + 5 + MAGIC_SZ]); - ut_error; - } - - uint min_key_version = mach_read_from_4 - (page + offset + MAGIC_SZ + 2 + iv_length); - - uint key_id = mach_read_from_4 - (page + offset + MAGIC_SZ + 2 + iv_length + 4); - - fil_encryption_t encryption = (fil_encryption_t)mach_read_from_1( - page + offset + MAGIC_SZ + 2 + iv_length + 8); - - crypt_data = fil_space_create_crypt_data(encryption, key_id); - /* We need to overwrite these as above function will initialize - members */ - crypt_data->type = type; - crypt_data->min_key_version = min_key_version; - crypt_data->page0_offset = offset; - memcpy(crypt_data->iv, page + offset + MAGIC_SZ + 2, iv_length); - - return crypt_data; -} - -/****************************************************************** -Free a crypt data object -@param[in,out] crypt_data crypt data to be freed */ -UNIV_INTERN -void -fil_space_destroy_crypt_data( - fil_space_crypt_t **crypt_data) -{ - if (crypt_data != NULL && (*crypt_data) != NULL) { - fil_space_crypt_t* c = *crypt_data; - c->~fil_space_crypt_t(); - mem_free(c); - *crypt_data = NULL; - } -} - -/****************************************************************** -Write crypt data to a page (0) -@param[in,out] page0 Page 0 where to write -@param[in,out] mtr Minitransaction */ -UNIV_INTERN -void -fil_space_crypt_t::write_page0( - byte* page, - mtr_t* mtr) -{ - ulint space_id = mach_read_from_4( - page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); - const uint len = sizeof(iv); - ulint zip_size = fsp_header_get_zip_size(page); - const ulint offset = fsp_header_get_crypt_offset(zip_size); - page0_offset = offset; - - /* - redo log this as bytewise updates to page 0 - followed by an MLOG_FILE_WRITE_CRYPT_DATA - (that will during recovery update fil_space_t) - */ - mlog_write_string(page + offset, CRYPT_MAGIC, MAGIC_SZ, mtr); - mlog_write_ulint(page + offset + MAGIC_SZ + 0, type, MLOG_1BYTE, mtr); - mlog_write_ulint(page + offset + MAGIC_SZ + 1, len, MLOG_1BYTE, mtr); - mlog_write_string(page + offset + MAGIC_SZ + 2, iv, len, - mtr); - mlog_write_ulint(page + offset + MAGIC_SZ + 2 + len, min_key_version, - MLOG_4BYTES, mtr); - mlog_write_ulint(page + offset + MAGIC_SZ + 2 + len + 4, key_id, - MLOG_4BYTES, mtr); - mlog_write_ulint(page + offset + MAGIC_SZ + 2 + len + 8, encryption, - MLOG_1BYTE, mtr); - - byte* log_ptr = mlog_open(mtr, 11 + 17 + len); - - if (log_ptr != NULL) { - log_ptr = mlog_write_initial_log_record_fast( - page, - MLOG_FILE_WRITE_CRYPT_DATA, - log_ptr, mtr); - mach_write_to_4(log_ptr, space_id); - log_ptr += 4; - mach_write_to_2(log_ptr, offset); - log_ptr += 2; - mach_write_to_1(log_ptr, type); - log_ptr += 1; - mach_write_to_1(log_ptr, len); - log_ptr += 1; - mach_write_to_4(log_ptr, min_key_version); - log_ptr += 4; - mach_write_to_4(log_ptr, key_id); - log_ptr += 4; - mach_write_to_1(log_ptr, encryption); - log_ptr += 1; - mlog_close(mtr, log_ptr); - - mlog_catenate_string(mtr, iv, len); - } -} - -/****************************************************************** -Set crypt data for a tablespace -@param[in,out] space Tablespace -@param[in,out] crypt_data Crypt data to be set -@return crypt_data in tablespace */ -static -fil_space_crypt_t* -fil_space_set_crypt_data( - fil_space_t* space, - fil_space_crypt_t* crypt_data) -{ - fil_space_crypt_t* free_crypt_data = NULL; - fil_space_crypt_t* ret_crypt_data = NULL; - - /* Provided space is protected using fil_space_acquire() - from concurrent operations. */ - if (space->crypt_data != NULL) { - /* There is already crypt data present, - merge new crypt_data */ - fil_space_merge_crypt_data(space->crypt_data, - crypt_data); - ret_crypt_data = space->crypt_data; - free_crypt_data = crypt_data; - } else { - space->crypt_data = crypt_data; - ret_crypt_data = space->crypt_data; - } - - if (free_crypt_data != NULL) { - /* there was already crypt data present and the new crypt - * data provided as argument to this function has been merged - * into that => free new crypt data - */ - fil_space_destroy_crypt_data(&free_crypt_data); - } - - return ret_crypt_data; -} - -/****************************************************************** -Parse a MLOG_FILE_WRITE_CRYPT_DATA log entry -@param[in] ptr Log entry start -@param[in] end_ptr Log entry end -@param[in] block buffer block -@return position on log buffer */ -UNIV_INTERN -byte* -fil_parse_write_crypt_data( - byte* ptr, - const byte* end_ptr, - const buf_block_t* block, - dberr_t* err) -{ - /* check that redo log entry is complete */ - size_t entry_size = - 4 + // size of space_id - 2 + // size of offset - 1 + // size of type - 1 + // size of iv-len - 4 + // size of min_key_version - 4 + // size of key_id - 1; // fil_encryption_t - - *err = DB_SUCCESS; - - if (ptr + entry_size > end_ptr) { - return NULL; - } - - ulint space_id = mach_read_from_4(ptr); - ptr += 4; - uint offset = mach_read_from_2(ptr); - ptr += 2; - uint type = mach_read_from_1(ptr); - ptr += 1; - size_t len = mach_read_from_1(ptr); - ptr += 1; - - ut_a(type == CRYPT_SCHEME_UNENCRYPTED || - type == CRYPT_SCHEME_1); // only supported - - ut_a(len == CRYPT_SCHEME_1_IV_LEN); // only supported - uint min_key_version = mach_read_from_4(ptr); - ptr += 4; - - uint key_id = mach_read_from_4(ptr); - ptr += 4; - - fil_encryption_t encryption = (fil_encryption_t)mach_read_from_1(ptr); - ptr +=1; - - if (ptr + len > end_ptr) { - return NULL; - } - - fil_space_crypt_t* crypt_data = fil_space_create_crypt_data(encryption, key_id); - /* Need to overwrite these as above will initialize fields. */ - crypt_data->page0_offset = offset; - crypt_data->min_key_version = min_key_version; - crypt_data->encryption = encryption; - memcpy(crypt_data->iv, ptr, len); - ptr += len; - - /* update fil_space memory cache with crypt_data */ - if (fil_space_t* space = fil_space_acquire_silent(space_id)) { - crypt_data = fil_space_set_crypt_data(space, crypt_data); - fil_space_release(space); - /* Check is used key found from encryption plugin */ - if (crypt_data->should_encrypt() - && !crypt_data->is_key_found()) { - *err = DB_DECRYPTION_FAILED; - } - } else { - fil_space_destroy_crypt_data(&crypt_data); - } - - return ptr; -} - -/****************************************************************** -Encrypt a buffer -@param[in,out] crypt_data Crypt data -@param[in] space space_id -@param[in] offset Page offset -@param[in] lsn Log sequence number -@param[in] src_frame Page to encrypt -@param[in] zip_size Compressed size or 0 -@param[in,out] dst_frame Output buffer -@return encrypted buffer or NULL */ -UNIV_INTERN -byte* -fil_encrypt_buf( - fil_space_crypt_t* crypt_data, - ulint space, - ulint offset, - lsn_t lsn, - const byte* src_frame, - ulint zip_size, - byte* dst_frame) -{ - ulint page_size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; - uint key_version = fil_crypt_get_latest_key_version(crypt_data); - - if (key_version == ENCRYPTION_KEY_VERSION_INVALID) { - ib_logf(IB_LOG_LEVEL_FATAL, - "Unknown key id %u. Can't continue!\n", - crypt_data->key_id); - ut_error; - } - - ulint orig_page_type = mach_read_from_2(src_frame+FIL_PAGE_TYPE); - ibool page_compressed = (orig_page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED); - ulint header_len = FIL_PAGE_DATA; - - if (page_compressed) { - header_len += (FIL_PAGE_COMPRESSED_SIZE + FIL_PAGE_COMPRESSION_METHOD_SIZE); - } - - /* FIL page header is not encrypted */ - memcpy(dst_frame, src_frame, header_len); - - /* Store key version */ - mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, key_version); - - /* Calculate the start offset in a page */ - ulint unencrypted_bytes = header_len + FIL_PAGE_DATA_END; - ulint srclen = page_size - unencrypted_bytes; - const byte* src = src_frame + header_len; - byte* dst = dst_frame + header_len; - uint32 dstlen = 0; - - if (page_compressed) { - srclen = mach_read_from_2(src_frame + FIL_PAGE_DATA); - } - - int rc = encryption_scheme_encrypt(src, srclen, dst, &dstlen, - crypt_data, key_version, - space, offset, lsn); - - if (! ((rc == MY_AES_OK) && ((ulint) dstlen == srclen))) { - ib_logf(IB_LOG_LEVEL_FATAL, - "Unable to encrypt data-block " - " src: %p srclen: %ld buf: %p buflen: %d." - " return-code: %d. Can't continue!\n", - src, (long)srclen, - dst, dstlen, rc); - ut_error; - } - - /* For compressed tables we do not store the FIL header because - the whole page is not stored to the disk. In compressed tables only - the FIL header + compressed (and now encrypted) payload alligned - to sector boundary is written. */ - if (!page_compressed) { - /* FIL page trailer is also not encrypted */ - memcpy(dst_frame + page_size - FIL_PAGE_DATA_END, - src_frame + page_size - FIL_PAGE_DATA_END, - FIL_PAGE_DATA_END); - } else { - /* Clean up rest of buffer */ - memset(dst_frame+header_len+srclen, 0, page_size - (header_len+srclen)); - } - - /* handle post encryption checksum */ - ib_uint32_t checksum = 0; - - checksum = fil_crypt_calculate_checksum(zip_size, dst_frame); - - // store the post-encryption checksum after the key-version - mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4, checksum); - - ut_ad(fil_space_verify_crypt_checksum(dst_frame, zip_size, NULL, offset)); - - srv_stats.pages_encrypted.inc(); - - return dst_frame; -} - -/****************************************************************** -Encrypt a page - -@param[in] space Tablespace -@param[in] offset Page offset -@param[in] lsn Log sequence number -@param[in] src_frame Page to encrypt -@param[in,out] dst_frame Output buffer -@return encrypted buffer or NULL */ -UNIV_INTERN -byte* -fil_space_encrypt( - const fil_space_t* space, - ulint offset, - lsn_t lsn, - byte* src_frame, - byte* dst_frame) -{ - ulint orig_page_type = mach_read_from_2(src_frame+FIL_PAGE_TYPE); - - if (orig_page_type==FIL_PAGE_TYPE_FSP_HDR - || orig_page_type==FIL_PAGE_TYPE_XDES) { - /* File space header or extent descriptor do not need to be - encrypted. */ - return (src_frame); - } - - if (!space->crypt_data || !space->crypt_data->is_encrypted()) { - return (src_frame); - } - - fil_space_crypt_t* crypt_data = space->crypt_data; - ut_ad(space->n_pending_ios > 0); - ulint zip_size = fsp_flags_get_zip_size(space->flags); - byte* tmp = fil_encrypt_buf(crypt_data, space->id, offset, lsn, src_frame, zip_size, dst_frame); - -#ifdef UNIV_DEBUG - if (tmp) { - /* Verify that encrypted buffer is not corrupted */ - byte* tmp_mem = (byte *)malloc(UNIV_PAGE_SIZE); - dberr_t err = DB_SUCCESS; - byte* src = src_frame; - bool page_compressed_encrypted = (mach_read_from_2(tmp+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED); - byte* comp_mem = NULL; - byte* uncomp_mem = NULL; - ulint size = (zip_size) ? zip_size : UNIV_PAGE_SIZE; - - if (page_compressed_encrypted) { - comp_mem = (byte *)malloc(UNIV_PAGE_SIZE); - uncomp_mem = (byte *)malloc(UNIV_PAGE_SIZE); - memcpy(comp_mem, src_frame, UNIV_PAGE_SIZE); - fil_decompress_page(uncomp_mem, comp_mem, - srv_page_size, NULL); - src = uncomp_mem; - } - - bool corrupted1 = buf_page_is_corrupted(true, src, zip_size, space); - bool ok = fil_space_decrypt(crypt_data, tmp_mem, size, tmp, &err); - - /* Need to decompress the page if it was also compressed */ - if (page_compressed_encrypted) { - memcpy(comp_mem, tmp_mem, UNIV_PAGE_SIZE); - fil_decompress_page(tmp_mem, comp_mem, - srv_page_size, NULL); - } - - bool corrupted = buf_page_is_corrupted(true, tmp_mem, zip_size, space); - memcpy(tmp_mem+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, src+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, 8); - bool different = memcmp(src, tmp_mem, size); - - if (!ok || corrupted || corrupted1 || err != DB_SUCCESS || different) { - fprintf(stderr, "ok %d corrupted %d corrupted1 %d err %d different %d\n", - ok , corrupted, corrupted1, err, different); - fprintf(stderr, "src_frame\n"); - buf_page_print(src_frame, zip_size, BUF_PAGE_PRINT_NO_CRASH); - fprintf(stderr, "encrypted_frame\n"); - buf_page_print(tmp, zip_size, BUF_PAGE_PRINT_NO_CRASH); - fprintf(stderr, "decrypted_frame\n"); - buf_page_print(tmp_mem, zip_size, 0); - } - - free(tmp_mem); - - if (comp_mem) { - free(comp_mem); - } - - if (uncomp_mem) { - free(uncomp_mem); - } - } - -#endif /* UNIV_DEBUG */ - - return tmp; -} - -/****************************************************************** -Decrypt a page -@param[in] crypt_data crypt_data -@param[in] tmp_frame Temporary buffer -@param[in] page_size Page size -@param[in,out] src_frame Page to decrypt -@param[out] err DB_SUCCESS or DB_DECRYPTION_FAILED -@return true if page decrypted, false if not.*/ -UNIV_INTERN -bool -fil_space_decrypt( - fil_space_crypt_t* crypt_data, - byte* tmp_frame, - ulint page_size, - byte* src_frame, - dberr_t* err) -{ - ulint page_type = mach_read_from_2(src_frame+FIL_PAGE_TYPE); - uint key_version = mach_read_from_4(src_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); - bool page_compressed = (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED); - ulint offset = mach_read_from_4(src_frame + FIL_PAGE_OFFSET); - ulint space = mach_read_from_4(src_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); - ib_uint64_t lsn = mach_read_from_8(src_frame + FIL_PAGE_LSN); - - *err = DB_SUCCESS; - - if (key_version == ENCRYPTION_KEY_NOT_ENCRYPTED) { - return false; - } - - ut_a(crypt_data != NULL && crypt_data->is_encrypted()); - - /* read space & lsn */ - ulint header_len = FIL_PAGE_DATA; - - if (page_compressed) { - header_len += (FIL_PAGE_COMPRESSED_SIZE + FIL_PAGE_COMPRESSION_METHOD_SIZE); - } - - /* Copy FIL page header, it is not encrypted */ - memcpy(tmp_frame, src_frame, header_len); - - /* Calculate the offset where decryption starts */ - const byte* src = src_frame + header_len; - byte* dst = tmp_frame + header_len; - uint32 dstlen = 0; - ulint srclen = page_size - (header_len + FIL_PAGE_DATA_END); - - if (page_compressed) { - srclen = mach_read_from_2(src_frame + FIL_PAGE_DATA); - } - - int rc = encryption_scheme_decrypt(src, srclen, dst, &dstlen, - crypt_data, key_version, - space, offset, lsn); - - if (! ((rc == MY_AES_OK) && ((ulint) dstlen == srclen))) { - - if (rc == -1) { - *err = DB_DECRYPTION_FAILED; - return false; - } - - ib_logf(IB_LOG_LEVEL_FATAL, - "Unable to decrypt data-block " - " src: %p srclen: %ld buf: %p buflen: %d." - " return-code: %d. Can't continue!\n", - src, (long)srclen, - dst, dstlen, rc); - ut_error; - } - - /* For compressed tables we do not store the FIL header because - the whole page is not stored to the disk. In compressed tables only - the FIL header + compressed (and now encrypted) payload alligned - to sector boundary is written. */ - if (!page_compressed) { - /* Copy FIL trailer */ - memcpy(tmp_frame + page_size - FIL_PAGE_DATA_END, - src_frame + page_size - FIL_PAGE_DATA_END, - FIL_PAGE_DATA_END); - } - - srv_stats.pages_decrypted.inc(); - - return true; /* page was decrypted */ -} - -/****************************************************************** -Decrypt a page -@param[in] space Tablespace -@param[in] tmp_frame Temporary buffer used for decrypting -@param[in] page_size Page size -@param[in,out] src_frame Page to decrypt -@param[out] decrypted true if page was decrypted -@return decrypted page, or original not encrypted page if decryption is -not needed.*/ -UNIV_INTERN -byte* -fil_space_decrypt( - const fil_space_t* space, - byte* tmp_frame, - byte* src_frame, - bool* decrypted) -{ - dberr_t err = DB_SUCCESS; - byte* res = NULL; - ulint zip_size = fsp_flags_get_zip_size(space->flags); - ulint size = zip_size ? zip_size : UNIV_PAGE_SIZE; - *decrypted = false; - - ut_ad(space->crypt_data != NULL && space->crypt_data->is_encrypted()); - ut_ad(space->n_pending_ios > 0); - - bool encrypted = fil_space_decrypt( - space->crypt_data, - tmp_frame, - size, - src_frame, - &err); - - if (err == DB_SUCCESS) { - if (encrypted) { - *decrypted = true; - /* Copy the decrypted page back to page buffer, not - really any other options. */ - memcpy(src_frame, tmp_frame, size); - } - - res = src_frame; - } - - return res; -} - -/****************************************************************** -Calculate post encryption checksum -@param[in] zip_size zip_size or 0 -@param[in] dst_frame Block where checksum is calculated -@return page checksum -not needed. */ -UNIV_INTERN -ulint -fil_crypt_calculate_checksum( - ulint zip_size, - const byte* dst_frame) -{ - ib_uint32_t checksum = 0; - - /* For encrypted tables we use only crc32 and strict_crc32 */ - if (zip_size == 0) { - checksum = buf_calc_page_crc32(dst_frame); - } else { - checksum = page_zip_calc_checksum(dst_frame, zip_size, - SRV_CHECKSUM_ALGORITHM_CRC32); - } - - return checksum; -} - -/********************************************************************* -Verify that post encryption checksum match calculated checksum. -This function should be called only if tablespace contains crypt_data -metadata (this is strong indication that tablespace is encrypted). -Function also verifies that traditional checksum does not match -calculated checksum as if it does page could be valid unencrypted, -encrypted, or corrupted. - -@param[in] page Page to verify -@param[in] zip_size zip size -@param[in] space Tablespace -@param[in] pageno Page no -@return true if page is encrypted AND OK, false otherwise */ -UNIV_INTERN -bool -fil_space_verify_crypt_checksum( - byte* page, - ulint zip_size, - const fil_space_t* space, - ulint pageno) -{ - uint key_version = mach_read_from_4(page+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); - - /* If page is not encrypted, return false */ - if (key_version == 0) { - return(false); - } - - /* Read stored post encryption checksum. */ - ib_uint32_t checksum = mach_read_from_4( - page + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4); - - /* Declare empty pages non-corrupted */ - if (checksum == 0 - && *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_LSN) == 0 - && buf_page_is_zeroes(page, zip_size)) { - return(true); - } - - /* Compressed and encrypted pages do not have checksum. Assume not - corrupted. Page verification happens after decompression in - buf_page_io_complete() using buf_page_is_corrupted(). */ - if (mach_read_from_2(page+FIL_PAGE_TYPE) == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) { - return (true); - } - - ib_uint32_t cchecksum1 = 0; - ib_uint32_t cchecksum2 = 0; - - /* Calculate checksums */ - if (zip_size) { - cchecksum1 = page_zip_calc_checksum( - page, zip_size, SRV_CHECKSUM_ALGORITHM_CRC32); - - if(cchecksum1 != checksum) { - cchecksum2 = page_zip_calc_checksum( - page, zip_size, - SRV_CHECKSUM_ALGORITHM_INNODB); - } - } else { - cchecksum1 = buf_calc_page_crc32(page); - - if (cchecksum1 != checksum) { - cchecksum2 = (ib_uint32_t) buf_calc_page_new_checksum( - page); - } - } - - /* If stored checksum matches one of the calculated checksums - page is not corrupted. */ - - bool encrypted = (checksum == cchecksum1 || checksum == cchecksum2 - || checksum == BUF_NO_CHECKSUM_MAGIC); - - /* MySQL 5.6 and MariaDB 10.0 and 10.1 will write an LSN to the - first page of each system tablespace file at - FIL_PAGE_FILE_FLUSH_LSN offset. On other pages and in other files, - the field might have been uninitialized until MySQL 5.5. In MySQL 5.7 - (and MariaDB Server 10.2.2) WL#7990 stopped writing the field for other - than page 0 of the system tablespace. - - Starting from MariaDB 10.1 the field has been repurposed for - encryption key_version. - - Starting with MySQL 5.7 (and MariaDB Server 10.2), the - field has been repurposed for SPATIAL INDEX pages for - FIL_RTREE_SPLIT_SEQ_NUM. - - Note that FIL_PAGE_FILE_FLUSH_LSN is not included in the InnoDB page - checksum. - - Thus, FIL_PAGE_FILE_FLUSH_LSN could contain any value. While the - field would usually be 0 for pages that are not encrypted, we cannot - assume that a nonzero value means that the page is encrypted. - Therefore we must validate the page both as encrypted and unencrypted - when FIL_PAGE_FILE_FLUSH_LSN does not contain 0. - */ - - ulint checksum1 = mach_read_from_4( - page + FIL_PAGE_SPACE_OR_CHKSUM); - - ulint checksum2 = checksum1; - - bool valid; - - if (zip_size) { - valid = (checksum1 == cchecksum1); - } else { - checksum1 = mach_read_from_4( - page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM); - valid = (buf_page_is_checksum_valid_crc32(page,checksum1,checksum2) - || buf_page_is_checksum_valid_innodb(page,checksum1, checksum2)); - } - - if (encrypted && valid) { - /* If page is encrypted and traditional checksums match, - page could be still encrypted, or not encrypted and valid or - corrupted. */ - ib_logf(IB_LOG_LEVEL_ERROR, - " Page %lu in space %s (%lu) maybe corrupted." - " Post encryption checksum %u stored [%lu:%lu] key_version %u", - pageno, - space ? space->name : "N/A", - mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID), - checksum, checksum1, checksum2, key_version); - encrypted = false; - } - - return(encrypted); -} - -/***********************************************************************/ - -/** A copy of global key state */ -struct key_state_t { - key_state_t() : key_id(0), key_version(0), - rotate_key_age(srv_fil_crypt_rotate_key_age) {} - bool operator==(const key_state_t& other) const { - return key_version == other.key_version && - rotate_key_age == other.rotate_key_age; - } - uint key_id; - uint key_version; - uint rotate_key_age; -}; - -/*********************************************************************** -Copy global key state -@param[in,out] new_state key state -@param[in] crypt_data crypt data */ -static void -fil_crypt_get_key_state( - key_state_t* new_state, - fil_space_crypt_t* crypt_data) -{ - if (srv_encrypt_tables) { - new_state->key_version = crypt_data->key_get_latest_version(); - new_state->rotate_key_age = srv_fil_crypt_rotate_key_age; - - ut_a(new_state->key_version != ENCRYPTION_KEY_NOT_ENCRYPTED); - } else { - new_state->key_version = 0; - new_state->rotate_key_age = 0; - } -} - -/*********************************************************************** -Check if a key needs rotation given a key_state -@param[in] encrypt_mode Encryption mode -@param[in] key_version Current key version -@param[in] latest_key_version Latest key version -@param[in] rotate_key_age when to rotate -@return true if key needs rotation, false if not */ -static bool -fil_crypt_needs_rotation( - fil_encryption_t encrypt_mode, - uint key_version, - uint latest_key_version, - uint rotate_key_age) -{ - if (key_version == ENCRYPTION_KEY_VERSION_INVALID) { - return false; - } - - if (key_version == 0 && latest_key_version != 0) { - /* this is rotation unencrypted => encrypted - * ignore rotate_key_age */ - return true; - } - - if (latest_key_version == 0 && key_version != 0) { - if (encrypt_mode == FIL_ENCRYPTION_DEFAULT) { - /* this is rotation encrypted => unencrypted */ - return true; - } - return false; - } - - /* this is rotation encrypted => encrypted, - * only reencrypt if key is sufficiently old */ - if (key_version + rotate_key_age < latest_key_version) { - return true; - } - - return false; -} - -/** Read page 0 and possible crypt data from there. -@param[in,out] space Tablespace */ -static inline -void -fil_crypt_read_crypt_data(fil_space_t* space) -{ - if (space->crypt_data || space->size) { - /* The encryption metadata has already been read, or - the tablespace is not encrypted and the file has been - opened already. */ - return; - } - - mtr_t mtr; - mtr_start(&mtr); - ulint zip_size = fsp_flags_get_zip_size(space->flags); - ulint offset = fsp_header_get_crypt_offset(zip_size); - if (buf_block_t* block = buf_page_get(space->id, zip_size, 0, - RW_S_LATCH, &mtr)) { - mutex_enter(&fil_system->mutex); - if (!space->crypt_data) { - space->crypt_data = fil_space_read_crypt_data( - space->id, block->frame, offset); - } - mutex_exit(&fil_system->mutex); - } - - mtr_commit(&mtr); -} - -/*********************************************************************** -Start encrypting a space -@param[in,out] space Tablespace -@return true if a recheck is needed */ -static -bool -fil_crypt_start_encrypting_space( - fil_space_t* space) -{ - bool recheck = false; - - mutex_enter(&fil_crypt_threads_mutex); - - fil_space_crypt_t *crypt_data = space->crypt_data; - - /* If space is not encrypted and encryption is not enabled, then - do not continue encrypting the space. */ - if (!crypt_data && !srv_encrypt_tables) { - mutex_exit(&fil_crypt_threads_mutex); - return false; - } - - if (crypt_data != NULL || fil_crypt_start_converting) { - /* someone beat us to it */ - if (fil_crypt_start_converting) { - recheck = true; - } - - mutex_exit(&fil_crypt_threads_mutex); - return recheck; - } - - /* NOTE: we need to write and flush page 0 before publishing - * the crypt data. This so that after restart there is no - * risk of finding encrypted pages without having - * crypt data in page 0 */ - - /* 1 - create crypt data */ - crypt_data = fil_space_create_crypt_data(FIL_ENCRYPTION_DEFAULT, FIL_DEFAULT_ENCRYPTION_KEY); - - if (crypt_data == NULL) { - mutex_exit(&fil_crypt_threads_mutex); - return false; - } - - crypt_data->type = CRYPT_SCHEME_UNENCRYPTED; - crypt_data->min_key_version = 0; // all pages are unencrypted - crypt_data->rotate_state.start_time = time(0); - crypt_data->rotate_state.starting = true; - crypt_data->rotate_state.active_threads = 1; - - mutex_enter(&crypt_data->mutex); - crypt_data = fil_space_set_crypt_data(space, crypt_data); - mutex_exit(&crypt_data->mutex); - - fil_crypt_start_converting = true; - mutex_exit(&fil_crypt_threads_mutex); - - do - { - mtr_t mtr; - mtr_start(&mtr); - - /* 2 - get page 0 */ - ulint zip_size = fsp_flags_get_zip_size(space->flags); - buf_block_t* block = buf_page_get_gen(space->id, zip_size, 0, - RW_X_LATCH, - NULL, - BUF_GET, - __FILE__, __LINE__, - &mtr); - - - /* 3 - write crypt data to page 0 */ - byte* frame = buf_block_get_frame(block); - crypt_data->type = CRYPT_SCHEME_1; - crypt_data->write_page0(frame, &mtr); - mtr_commit(&mtr); - - /* record lsn of update */ - lsn_t end_lsn = mtr.end_lsn; - - /* 4 - sync tablespace before publishing crypt data */ - - bool success = false; - ulint sum_pages = 0; - - do { - ulint n_pages = 0; - success = buf_flush_list(ULINT_MAX, end_lsn, &n_pages); - buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST); - sum_pages += n_pages; - } while (!success); - - /* 5 - publish crypt data */ - mutex_enter(&fil_crypt_threads_mutex); - mutex_enter(&crypt_data->mutex); - crypt_data->type = CRYPT_SCHEME_1; - ut_a(crypt_data->rotate_state.active_threads == 1); - crypt_data->rotate_state.active_threads = 0; - crypt_data->rotate_state.starting = false; - - fil_crypt_start_converting = false; - mutex_exit(&crypt_data->mutex); - mutex_exit(&fil_crypt_threads_mutex); - - return recheck; - } while (0); - - mutex_enter(&crypt_data->mutex); - ut_a(crypt_data->rotate_state.active_threads == 1); - crypt_data->rotate_state.active_threads = 0; - mutex_exit(&crypt_data->mutex); - - mutex_enter(&fil_crypt_threads_mutex); - fil_crypt_start_converting = false; - mutex_exit(&fil_crypt_threads_mutex); - - return recheck; -} - -/** State of a rotation thread */ -struct rotate_thread_t { - explicit rotate_thread_t(uint no) { - memset(this, 0, sizeof(* this)); - thread_no = no; - first = true; - estimated_max_iops = 20; - } - - uint thread_no; - bool first; /*!< is position before first space */ - fil_space_t* space; /*!< current space or NULL */ - ulint offset; /*!< current offset */ - ulint batch; /*!< #pages to rotate */ - uint min_key_version_found;/*!< min key version found but not rotated */ - lsn_t end_lsn; /*!< max lsn when rotating this space */ - - uint estimated_max_iops; /*!< estimation of max iops */ - uint allocated_iops; /*!< allocated iops */ - uint cnt_waited; /*!< #times waited during this slot */ - uint sum_waited_us; /*!< wait time during this slot */ - - fil_crypt_stat_t crypt_stat; // statistics - - btr_scrub_t scrub_data; /* thread local data used by btr_scrub-functions - * when iterating pages of tablespace */ - - /** @return whether this thread should terminate */ - bool should_shutdown() const { - switch (srv_shutdown_state) { - case SRV_SHUTDOWN_NONE: - return thread_no >= srv_n_fil_crypt_threads; - case SRV_SHUTDOWN_CLEANUP: - return true; - case SRV_SHUTDOWN_FLUSH_PHASE: - case SRV_SHUTDOWN_LAST_PHASE: - case SRV_SHUTDOWN_EXIT_THREADS: - break; - } - ut_ad(0); - return true; - } -}; - -/*********************************************************************** -Check if space needs rotation given a key_state -@param[in,out] state Key rotation state -@param[in,out] key_state Key state -@param[in,out] recheck needs recheck ? -@return true if space needs key rotation */ -static -bool -fil_crypt_space_needs_rotation( - rotate_thread_t* state, - key_state_t* key_state, - bool* recheck) -{ - fil_space_t* space = state->space; - - /* Make sure that tablespace is normal tablespace */ - if (space->purpose != FIL_TABLESPACE) { - return false; - } - - ut_ad(space->n_pending_ops > 0); - - fil_space_crypt_t *crypt_data = space->crypt_data; - - if (crypt_data == NULL) { - /** - * space has no crypt data - * start encrypting it... - */ - *recheck = fil_crypt_start_encrypting_space(space); - crypt_data = space->crypt_data; - - if (crypt_data == NULL) { - return false; - } - - crypt_data->key_get_latest_version(); - } - - /* If used key_id is not found from encryption plugin we can't - continue to rotate the tablespace */ - if (!crypt_data->is_key_found()) { - return false; - } - - mutex_enter(&crypt_data->mutex); - - do { - /* prevent threads from starting to rotate space */ - if (crypt_data->rotate_state.starting) { - /* recheck this space later */ - *recheck = true; - break; - } - - /* prevent threads from starting to rotate space */ - if (space->is_stopping()) { - break; - } - - if (crypt_data->rotate_state.flushing) { - break; - } - - /* No need to rotate space if encryption is disabled */ - if (crypt_data->not_encrypted()) { - break; - } - - if (crypt_data->key_id != key_state->key_id) { - key_state->key_id= crypt_data->key_id; - fil_crypt_get_key_state(key_state, crypt_data); - } - - bool need_key_rotation = fil_crypt_needs_rotation( - crypt_data->encryption, - crypt_data->min_key_version, - key_state->key_version, key_state->rotate_key_age); - - crypt_data->rotate_state.scrubbing.is_active = - btr_scrub_start_space(space->id, &state->scrub_data); - - time_t diff = time(0) - crypt_data->rotate_state.scrubbing. - last_scrub_completed; - - bool need_scrubbing = - (srv_background_scrub_data_uncompressed || - srv_background_scrub_data_compressed) && - crypt_data->rotate_state.scrubbing.is_active - && diff >= 0 - && ulint(diff) >= srv_background_scrub_data_interval; - - if (need_key_rotation == false && need_scrubbing == false) { - break; - } - - mutex_exit(&crypt_data->mutex); - - return true; - } while (0); - - mutex_exit(&crypt_data->mutex); - - - return false; -} - -/*********************************************************************** -Update global statistics with thread statistics -@param[in,out] state key rotation statistics */ -static void -fil_crypt_update_total_stat( - rotate_thread_t *state) -{ - mutex_enter(&crypt_stat_mutex); - crypt_stat.pages_read_from_cache += - state->crypt_stat.pages_read_from_cache; - crypt_stat.pages_read_from_disk += - state->crypt_stat.pages_read_from_disk; - crypt_stat.pages_modified += state->crypt_stat.pages_modified; - crypt_stat.pages_flushed += state->crypt_stat.pages_flushed; - // remote old estimate - crypt_stat.estimated_iops -= state->crypt_stat.estimated_iops; - // add new estimate - crypt_stat.estimated_iops += state->estimated_max_iops; - mutex_exit(&crypt_stat_mutex); - - // make new estimate "current" estimate - memset(&state->crypt_stat, 0, sizeof(state->crypt_stat)); - // record our old (current) estimate - state->crypt_stat.estimated_iops = state->estimated_max_iops; -} - -/*********************************************************************** -Allocate iops to thread from global setting, -used before starting to rotate a space. -@param[in,out] state Rotation state -@return true if allocation succeeded, false if failed */ -static -bool -fil_crypt_alloc_iops( - rotate_thread_t *state) -{ - ut_ad(state->allocated_iops == 0); - - /* We have not yet selected the space to rotate, thus - state might not contain space and we can't check - its status yet. */ - - uint max_iops = state->estimated_max_iops; - mutex_enter(&fil_crypt_threads_mutex); - - if (n_fil_crypt_iops_allocated >= srv_n_fil_crypt_iops) { - /* this can happen when user decreases srv_fil_crypt_iops */ - mutex_exit(&fil_crypt_threads_mutex); - return false; - } - - uint alloc = srv_n_fil_crypt_iops - n_fil_crypt_iops_allocated; - - if (alloc > max_iops) { - alloc = max_iops; - } - - n_fil_crypt_iops_allocated += alloc; - mutex_exit(&fil_crypt_threads_mutex); - - state->allocated_iops = alloc; - - return alloc > 0; -} - -/*********************************************************************** -Reallocate iops to thread, -used when inside a space -@param[in,out] state Rotation state */ -static -void -fil_crypt_realloc_iops( - rotate_thread_t *state) -{ - ut_a(state->allocated_iops > 0); - - if (10 * state->cnt_waited > state->batch) { - /* if we waited more than 10% re-estimate max_iops */ - ulint avg_wait_time_us = - state->sum_waited_us / state->cnt_waited; - - if (avg_wait_time_us == 0) { - avg_wait_time_us = 1; // prevent division by zero - } - - DBUG_PRINT("ib_crypt", - ("thr_no: %u - update estimated_max_iops from %u to " - ULINTPF ".", - state->thread_no, - state->estimated_max_iops, - 1000000 / avg_wait_time_us)); - - state->estimated_max_iops = uint(1000000 / avg_wait_time_us); - state->cnt_waited = 0; - state->sum_waited_us = 0; - } else { - - DBUG_PRINT("ib_crypt", - ("thr_no: %u only waited %lu%% skip re-estimate.", - state->thread_no, - (100 * state->cnt_waited) / state->batch)); - } - - if (state->estimated_max_iops <= state->allocated_iops) { - /* return extra iops */ - uint extra = state->allocated_iops - state->estimated_max_iops; - - if (extra > 0) { - mutex_enter(&fil_crypt_threads_mutex); - if (n_fil_crypt_iops_allocated < extra) { - /* unknown bug! - * crash in debug - * keep n_fil_crypt_iops_allocated unchanged - * in release */ - ut_ad(0); - extra = 0; - } - n_fil_crypt_iops_allocated -= extra; - state->allocated_iops -= extra; - - if (state->allocated_iops == 0) { - /* no matter how slow io system seems to be - * never decrease allocated_iops to 0... */ - state->allocated_iops ++; - n_fil_crypt_iops_allocated ++; - } - - os_event_set(fil_crypt_threads_event); - mutex_exit(&fil_crypt_threads_mutex); - } - } else { - /* see if there are more to get */ - mutex_enter(&fil_crypt_threads_mutex); - if (n_fil_crypt_iops_allocated < srv_n_fil_crypt_iops) { - /* there are extra iops free */ - uint extra = srv_n_fil_crypt_iops - - n_fil_crypt_iops_allocated; - if (state->allocated_iops + extra > - state->estimated_max_iops) { - /* but don't alloc more than our max */ - extra = state->estimated_max_iops - - state->allocated_iops; - } - n_fil_crypt_iops_allocated += extra; - state->allocated_iops += extra; - - DBUG_PRINT("ib_crypt", - ("thr_no: %u increased iops from %u to %u.", - state->thread_no, - state->allocated_iops - extra, - state->allocated_iops)); - - } - mutex_exit(&fil_crypt_threads_mutex); - } - - fil_crypt_update_total_stat(state); -} - -/*********************************************************************** -Return allocated iops to global -@param[in,out] state Rotation state */ -static -void -fil_crypt_return_iops( - rotate_thread_t *state) -{ - if (state->allocated_iops > 0) { - uint iops = state->allocated_iops; - mutex_enter(&fil_crypt_threads_mutex); - if (n_fil_crypt_iops_allocated < iops) { - /* unknown bug! - * crash in debug - * keep n_fil_crypt_iops_allocated unchanged - * in release */ - ut_ad(0); - iops = 0; - } - - n_fil_crypt_iops_allocated -= iops; - state->allocated_iops = 0; - os_event_set(fil_crypt_threads_event); - mutex_exit(&fil_crypt_threads_mutex); - } - - fil_crypt_update_total_stat(state); -} - -/*********************************************************************** -Search for a space needing rotation -@param[in,out] key_state Key state -@param[in,out] state Rotation state -@param[in,out] recheck recheck ? */ -static -bool -fil_crypt_find_space_to_rotate( - key_state_t* key_state, - rotate_thread_t* state, - bool* recheck) -{ - /* we need iops to start rotating */ - while (!state->should_shutdown() && !fil_crypt_alloc_iops(state)) { - os_event_reset(fil_crypt_threads_event); - os_event_wait_time(fil_crypt_threads_event, 1000000); - } - - if (state->should_shutdown()) { - if (state->space) { - fil_space_release(state->space); - state->space = NULL; - } - return false; - } - - if (state->first) { - state->first = false; - if (state->space) { - fil_space_release(state->space); - } - state->space = NULL; - } - - /* If key rotation is enabled (default) we iterate all tablespaces. - If key rotation is not enabled we iterate only the tablespaces - added to keyrotation list. */ - if (srv_fil_crypt_rotate_key_age) { - state->space = fil_space_next(state->space); - } else { - state->space = fil_space_keyrotate_next(state->space); - } - - while (!state->should_shutdown() && state->space) { - fil_crypt_read_crypt_data(state->space); - - if (fil_crypt_space_needs_rotation(state, key_state, recheck)) { - ut_ad(key_state->key_id); - /* init state->min_key_version_found before - * starting on a space */ - state->min_key_version_found = key_state->key_version; - return true; - } - - if (srv_fil_crypt_rotate_key_age) { - state->space = fil_space_next(state->space); - } else { - state->space = fil_space_keyrotate_next(state->space); - } - } - - /* if we didn't find any space return iops */ - fil_crypt_return_iops(state); - - return false; - -} - -/*********************************************************************** -Start rotating a space -@param[in] key_state Key state -@param[in,out] state Rotation state */ -static -void -fil_crypt_start_rotate_space( - const key_state_t* key_state, - rotate_thread_t* state) -{ - fil_space_crypt_t *crypt_data = state->space->crypt_data; - - ut_ad(crypt_data); - mutex_enter(&crypt_data->mutex); - ut_ad(key_state->key_id == crypt_data->key_id); - - if (crypt_data->rotate_state.active_threads == 0) { - /* only first thread needs to init */ - crypt_data->rotate_state.next_offset = 1; // skip page 0 - /* no need to rotate beyond current max - * if space extends, it will be encrypted with newer version */ - /* FIXME: max_offset could be removed and instead - space->size consulted.*/ - crypt_data->rotate_state.max_offset = state->space->size; - crypt_data->rotate_state.end_lsn = 0; - crypt_data->rotate_state.min_key_version_found = - key_state->key_version; - - crypt_data->rotate_state.start_time = time(0); - - if (crypt_data->type == CRYPT_SCHEME_UNENCRYPTED && - crypt_data->is_encrypted() && - key_state->key_version != 0) { - /* this is rotation unencrypted => encrypted */ - crypt_data->type = CRYPT_SCHEME_1; - } - } - - /* count active threads in space */ - crypt_data->rotate_state.active_threads++; - - /* Initialize thread local state */ - state->end_lsn = crypt_data->rotate_state.end_lsn; - state->min_key_version_found = - crypt_data->rotate_state.min_key_version_found; - - mutex_exit(&crypt_data->mutex); -} - -/*********************************************************************** -Search for batch of pages needing rotation -@param[in] key_state Key state -@param[in,out] state Rotation state -@return true if page needing key rotation found, false if not found */ -static -bool -fil_crypt_find_page_to_rotate( - const key_state_t* key_state, - rotate_thread_t* state) -{ - ulint batch = srv_alloc_time * state->allocated_iops; - fil_space_t* space = state->space; - - ut_ad(!space || space->n_pending_ops > 0); - - /* If space is marked to be dropped stop rotation. */ - if (!space || space->is_stopping()) { - return false; - } - - fil_space_crypt_t *crypt_data = space->crypt_data; - - mutex_enter(&crypt_data->mutex); - ut_ad(key_state->key_id == crypt_data->key_id); - - bool found = crypt_data->rotate_state.max_offset >= - crypt_data->rotate_state.next_offset; - - if (found) { - state->offset = crypt_data->rotate_state.next_offset; - ulint remaining = crypt_data->rotate_state.max_offset - - crypt_data->rotate_state.next_offset; - - if (batch <= remaining) { - state->batch = batch; - } else { - state->batch = remaining; - } - } - - crypt_data->rotate_state.next_offset += batch; - mutex_exit(&crypt_data->mutex); - return found; -} - -/*********************************************************************** -Check if a page is uninitialized (doesn't need to be rotated) -@param[in] frame Page to check -@param[in] zip_size zip_size or 0 -@return true if page is uninitialized, false if not. */ -static inline -bool -fil_crypt_is_page_uninitialized( - const byte *frame, - uint zip_size) -{ - return (buf_page_is_zeroes(frame, zip_size)); -} - -#define fil_crypt_get_page_throttle(state,offset,mtr,sleeptime_ms) \ - fil_crypt_get_page_throttle_func(state, offset, mtr, \ - sleeptime_ms, __FILE__, __LINE__) - -/*********************************************************************** -Get a page and compute sleep time -@param[in,out] state Rotation state -@param[in] zip_size compressed size or 0 -@param[in] offset Page offset -@param[in,out] mtr Minitransaction -@param[out] sleeptime_ms Sleep time -@param[in] file File where called -@param[in] line Line where called -@return page or NULL*/ -static -buf_block_t* -fil_crypt_get_page_throttle_func( - rotate_thread_t* state, - ulint offset, - mtr_t* mtr, - ulint* sleeptime_ms, - const char* file, - ulint line) -{ - fil_space_t* space = state->space; - ulint zip_size = fsp_flags_get_zip_size(space->flags); - ut_ad(space->n_pending_ops > 0); - - buf_block_t* block = buf_page_try_get_func(space->id, offset, RW_X_LATCH, - true, - file, line, mtr); - if (block != NULL) { - /* page was in buffer pool */ - state->crypt_stat.pages_read_from_cache++; - return block; - } - - /* Before reading from tablespace we need to make sure that - tablespace exists and is not is just being dropped. */ - if (space->is_stopping()) { - return NULL; - } - - state->crypt_stat.pages_read_from_disk++; - - ullint start = ut_time_us(NULL); - block = buf_page_get_gen(space->id, zip_size, offset, - RW_X_LATCH, - NULL, BUF_GET_POSSIBLY_FREED, - file, line, mtr); - ullint end = ut_time_us(NULL); - - if (end < start) { - end = start; // safety... - } - - state->cnt_waited++; - state->sum_waited_us += (end - start); - - /* average page load */ - ulint add_sleeptime_ms = 0; - ulint avg_wait_time_us = state->sum_waited_us / state->cnt_waited; - ulint alloc_wait_us = 1000000 / state->allocated_iops; - - if (avg_wait_time_us < alloc_wait_us) { - /* we reading faster than we allocated */ - add_sleeptime_ms = (alloc_wait_us - avg_wait_time_us) / 1000; - } else { - /* if page load time is longer than we want, skip sleeping */ - } - - *sleeptime_ms += add_sleeptime_ms; - - return block; -} - - -/*********************************************************************** -Get block and allocation status - -note: innodb locks fil_space_latch and then block when allocating page -but locks block and then fil_space_latch when freeing page. - -@param[in,out] state Rotation state -@param[in] zip_size Compressed size or 0 -@param[in] offset Page offset -@param[in,out] mtr Minitransaction -@param[out] allocation_status Allocation status -@param[out] sleeptime_ms Sleep time -@return block or NULL -*/ -static -buf_block_t* -btr_scrub_get_block_and_allocation_status( - rotate_thread_t* state, - uint zip_size, - ulint offset, - mtr_t* mtr, - btr_scrub_page_allocation_status_t *allocation_status, - ulint* sleeptime_ms) -{ - mtr_t local_mtr; - buf_block_t *block = NULL; - fil_space_t* space = state->space; - - ut_ad(space->n_pending_ops > 0); - ut_ad(zip_size == fsp_flags_get_zip_size(space->flags)); - - mtr_start(&local_mtr); - - *allocation_status = fsp_page_is_free(space->id, offset, &local_mtr) ? - BTR_SCRUB_PAGE_FREE : - BTR_SCRUB_PAGE_ALLOCATED; - - if (*allocation_status == BTR_SCRUB_PAGE_FREE) { - /* this is easy case, we lock fil_space_latch first and - then block */ - block = fil_crypt_get_page_throttle(state, - offset, mtr, - sleeptime_ms); - mtr_commit(&local_mtr); - } else { - /* page is allocated according to xdes */ - - /* release fil_space_latch *before* fetching block */ - mtr_commit(&local_mtr); - - /* NOTE: when we have locked dict_index_get_lock(), - * it's safe to release fil_space_latch and then fetch block - * as dict_index_get_lock() is needed to make tree modifications - * such as free-ing a page - */ - - block = fil_crypt_get_page_throttle(state, - offset, mtr, - sleeptime_ms); - } - - return block; -} - - -/*********************************************************************** -Rotate one page -@param[in,out] key_state Key state -@param[in,out] state Rotation state */ -static -void -fil_crypt_rotate_page( - const key_state_t* key_state, - rotate_thread_t* state) -{ - fil_space_t*space = state->space; - ulint space_id = space->id; - ulint offset = state->offset; - const uint zip_size = fsp_flags_get_zip_size(space->flags); - ulint sleeptime_ms = 0; - fil_space_crypt_t *crypt_data = space->crypt_data; - - ut_ad(space->n_pending_ops > 0); - - /* In fil_crypt_thread where key rotation is done we have - acquired space and checked that this space is not yet - marked to be dropped. Similarly, in fil_crypt_find_page_to_rotate(). - Check here also to give DROP TABLE or similar a change. */ - if (space->is_stopping()) { - return; - } - - if (space_id == TRX_SYS_SPACE && offset == TRX_SYS_PAGE_NO) { - /* don't encrypt this as it contains address to dblwr buffer */ - return; - } - - mtr_t mtr; - mtr_start(&mtr); - buf_block_t* block = fil_crypt_get_page_throttle(state, - offset, &mtr, - &sleeptime_ms); - - if (block) { - - bool modified = false; - int needs_scrubbing = BTR_SCRUB_SKIP_PAGE; - lsn_t block_lsn = block->page.newest_modification; - byte* frame = buf_block_get_frame(block); - uint kv = mach_read_from_4(frame+FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); - - /* check if tablespace is closing after reading page */ - if (!space->is_stopping()) { - - if (kv == 0 && - fil_crypt_is_page_uninitialized(frame, zip_size)) { - ; - } else if (fil_crypt_needs_rotation( - crypt_data->encryption, - kv, key_state->key_version, - key_state->rotate_key_age)) { - - modified = true; - - /* force rotation by dummy updating page */ - mlog_write_ulint(frame + - FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, - space_id, MLOG_4BYTES, &mtr); - - /* statistics */ - state->crypt_stat.pages_modified++; - } else { - if (crypt_data->is_encrypted()) { - if (kv < state->min_key_version_found) { - state->min_key_version_found = kv; - } - } - } - - needs_scrubbing = btr_page_needs_scrubbing( - &state->scrub_data, block, - BTR_SCRUB_PAGE_ALLOCATION_UNKNOWN); - } - - mtr_commit(&mtr); - lsn_t end_lsn = mtr.end_lsn; - - if (needs_scrubbing == BTR_SCRUB_PAGE) { - mtr_start(&mtr); - /* - * refetch page and allocation status - */ - btr_scrub_page_allocation_status_t allocated; - block = btr_scrub_get_block_and_allocation_status( - state, zip_size, offset, &mtr, - &allocated, - &sleeptime_ms); - - if (block) { - - /* get required table/index and index-locks */ - needs_scrubbing = btr_scrub_recheck_page( - &state->scrub_data, block, allocated, &mtr); - - if (needs_scrubbing == BTR_SCRUB_PAGE) { - /* we need to refetch it once more now that we have - * index locked */ - block = btr_scrub_get_block_and_allocation_status( - state, zip_size, offset, &mtr, - &allocated, - &sleeptime_ms); - - needs_scrubbing = btr_scrub_page(&state->scrub_data, - block, allocated, - &mtr); - } - - /* NOTE: mtr is committed inside btr_scrub_recheck_page() - * and/or btr_scrub_page. This is to make sure that - * locks & pages are latched in corrected order, - * the mtr is in some circumstances restarted. - * (mtr_commit() + mtr_start()) - */ - } - } - - if (needs_scrubbing != BTR_SCRUB_PAGE) { - /* if page didn't need scrubbing it might be that cleanups - are needed. do those outside of any mtr to prevent deadlocks. - - the information what kinds of cleanups that are needed are - encoded inside the needs_scrubbing, but this is opaque to - this function (except the value BTR_SCRUB_PAGE) */ - btr_scrub_skip_page(&state->scrub_data, needs_scrubbing); - } - - if (needs_scrubbing == BTR_SCRUB_TURNED_OFF) { - /* if we just detected that scrubbing was turned off - * update global state to reflect this */ - ut_ad(crypt_data); - mutex_enter(&crypt_data->mutex); - crypt_data->rotate_state.scrubbing.is_active = false; - mutex_exit(&crypt_data->mutex); - } - - if (modified) { - /* if we modified page, we take lsn from mtr */ - ut_a(end_lsn > state->end_lsn); - ut_a(end_lsn > block_lsn); - state->end_lsn = end_lsn; - } else { - /* if we did not modify page, check for max lsn */ - if (block_lsn > state->end_lsn) { - state->end_lsn = block_lsn; - } - } - } else { - /* If block read failed mtr memo and log should be empty. */ - ut_ad(dyn_array_get_data_size(&mtr.memo) == 0); - ut_ad(dyn_array_get_data_size(&mtr.log) == 0); - mtr_commit(&mtr); - } - - if (sleeptime_ms) { - os_event_reset(fil_crypt_throttle_sleep_event); - os_event_wait_time(fil_crypt_throttle_sleep_event, - 1000 * sleeptime_ms); - } -} - -/*********************************************************************** -Rotate a batch of pages -@param[in,out] key_state Key state -@param[in,out] state Rotation state */ -static -void -fil_crypt_rotate_pages( - const key_state_t* key_state, - rotate_thread_t* state) -{ - ulint space = state->space->id; - ulint end = state->offset + state->batch; - - ut_ad(state->space->n_pending_ops > 0); - - for (; state->offset < end; state->offset++) { - - /* we can't rotate pages in dblwr buffer as - * it's not possible to read those due to lots of asserts - * in buffer pool. - * - * However since these are only (short-lived) copies of - * real pages, they will be updated anyway when the - * real page is updated - */ - if (space == TRX_SYS_SPACE && - buf_dblwr_page_inside(state->offset)) { - continue; - } - - fil_crypt_rotate_page(key_state, state); - } -} - -/*********************************************************************** -Flush rotated pages and then update page 0 - -@param[in,out] state rotation state */ -static -void -fil_crypt_flush_space( - rotate_thread_t* state) -{ - fil_space_t* space = state->space; - fil_space_crypt_t *crypt_data = space->crypt_data; - - ut_ad(space->n_pending_ops > 0); - - /* flush tablespace pages so that there are no pages left with old key */ - lsn_t end_lsn = crypt_data->rotate_state.end_lsn; - - if (end_lsn > 0 && !space->is_stopping()) { - bool success = false; - ulint n_pages = 0; - ulint sum_pages = 0; - ullint start = ut_time_us(NULL); - - do { - success = buf_flush_list(ULINT_MAX, end_lsn, &n_pages); - buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST); - sum_pages += n_pages; - } while (!success && !space->is_stopping()); - - ullint end = ut_time_us(NULL); - - if (sum_pages && end > start) { - state->cnt_waited += sum_pages; - state->sum_waited_us += (end - start); - - /* statistics */ - state->crypt_stat.pages_flushed += sum_pages; - } - } - - if (crypt_data->min_key_version == 0) { - crypt_data->type = CRYPT_SCHEME_UNENCRYPTED; - } - - /* update page 0 */ - mtr_t mtr; - mtr_start(&mtr); - - const uint zip_size = fsp_flags_get_zip_size(state->space->flags); - - buf_block_t* block = buf_page_get_gen(space->id, zip_size, 0, - RW_X_LATCH, NULL, BUF_GET, - __FILE__, __LINE__, &mtr); - byte* frame = buf_block_get_frame(block); - - crypt_data->write_page0(frame, &mtr); - - mtr_commit(&mtr); -} - -/*********************************************************************** -Complete rotating a space -@param[in,out] key_state Key state -@param[in,out] state Rotation state */ -static -void -fil_crypt_complete_rotate_space( - const key_state_t* key_state, - rotate_thread_t* state) -{ - fil_space_crypt_t *crypt_data = state->space->crypt_data; - - ut_ad(crypt_data); - ut_ad(state->space->n_pending_ops > 0); - - /* Space might already be dropped */ - if (!state->space->is_stopping()) { - mutex_enter(&crypt_data->mutex); - - /** - * Update crypt data state with state from thread - */ - if (state->min_key_version_found < - crypt_data->rotate_state.min_key_version_found) { - crypt_data->rotate_state.min_key_version_found = - state->min_key_version_found; - } - - if (state->end_lsn > crypt_data->rotate_state.end_lsn) { - crypt_data->rotate_state.end_lsn = state->end_lsn; - } - - ut_a(crypt_data->rotate_state.active_threads > 0); - crypt_data->rotate_state.active_threads--; - bool last = crypt_data->rotate_state.active_threads == 0; - - /** - * check if space is fully done - * this as when threads shutdown, it could be that we "complete" - * iterating before we have scanned the full space. - */ - bool done = crypt_data->rotate_state.next_offset >= - crypt_data->rotate_state.max_offset; - - /** - * we should flush space if we're last thread AND - * the iteration is done - */ - bool should_flush = last && done; - - if (should_flush) { - /* we're the last active thread */ - crypt_data->rotate_state.flushing = true; - crypt_data->min_key_version = - crypt_data->rotate_state.min_key_version_found; - } - - /* inform scrubbing */ - crypt_data->rotate_state.scrubbing.is_active = false; - mutex_exit(&crypt_data->mutex); - - /* all threads must call btr_scrub_complete_space wo/ mutex held */ - if (btr_scrub_complete_space(&state->scrub_data) == true) { - if (should_flush) { - /* only last thread updates last_scrub_completed */ - ut_ad(crypt_data); - mutex_enter(&crypt_data->mutex); - crypt_data->rotate_state.scrubbing. - last_scrub_completed = time(0); - mutex_exit(&crypt_data->mutex); - } - } - - if (should_flush) { - fil_crypt_flush_space(state); - - mutex_enter(&crypt_data->mutex); - crypt_data->rotate_state.flushing = false; - mutex_exit(&crypt_data->mutex); - } - } else { - mutex_enter(&crypt_data->mutex); - ut_a(crypt_data->rotate_state.active_threads > 0); - crypt_data->rotate_state.active_threads--; - mutex_exit(&crypt_data->mutex); - } -} - -/*********************************************************************//** -A thread which monitors global key state and rotates tablespaces accordingly -@return a dummy parameter */ -extern "C" UNIV_INTERN -os_thread_ret_t -DECLARE_THREAD(fil_crypt_thread)( -/*=============================*/ - void* arg __attribute__((unused))) /*!< in: a dummy parameter required - * by os_thread_create */ -{ - UT_NOT_USED(arg); - - mutex_enter(&fil_crypt_threads_mutex); - uint thread_no = srv_n_fil_crypt_threads_started; - srv_n_fil_crypt_threads_started++; - os_event_set(fil_crypt_event); /* signal that we started */ - mutex_exit(&fil_crypt_threads_mutex); - - /* state of this thread */ - rotate_thread_t thr(thread_no); - - /* if we find a space that is starting, skip over it and recheck it later */ - bool recheck = false; - - while (!thr.should_shutdown()) { - - key_state_t new_state; - - time_t wait_start = time(0); - - while (!thr.should_shutdown()) { - - /* wait for key state changes - * i.e either new key version of change or - * new rotate_key_age */ - os_event_reset(fil_crypt_threads_event); - - if (os_event_wait_time(fil_crypt_threads_event, 1000000) == 0) { - break; - } - - if (recheck) { - /* check recheck here, after sleep, so - * that we don't busy loop while when one thread is starting - * a space*/ - break; - } - - time_t waited = time(0) - wait_start; - - /* Break if we have waited the background scrub - internal and background scrubbing is enabled */ - if (waited >= 0 - && ulint(waited) >= srv_background_scrub_data_check_interval - && (srv_background_scrub_data_uncompressed - || srv_background_scrub_data_compressed)) { - break; - } - } - - recheck = false; - thr.first = true; // restart from first tablespace - - /* iterate all spaces searching for those needing rotation */ - while (!thr.should_shutdown() && - fil_crypt_find_space_to_rotate(&new_state, &thr, &recheck)) { - - /* we found a space to rotate */ - fil_crypt_start_rotate_space(&new_state, &thr); - - /* iterate all pages (cooperativly with other threads) */ - while (!thr.should_shutdown() && - fil_crypt_find_page_to_rotate(&new_state, &thr)) { - - if (!thr.space->is_stopping()) { - /* rotate a (set) of pages */ - fil_crypt_rotate_pages(&new_state, &thr); - } - - /* If space is marked as stopping, release - space and stop rotation. */ - if (thr.space->is_stopping()) { - fil_crypt_complete_rotate_space( - &new_state, &thr); - fil_space_release(thr.space); - thr.space = NULL; - break; - } - - /* realloc iops */ - fil_crypt_realloc_iops(&thr); - } - - /* complete rotation */ - if (thr.space) { - fil_crypt_complete_rotate_space(&new_state, &thr); - } - - /* force key state refresh */ - new_state.key_id = 0; - - /* return iops */ - fil_crypt_return_iops(&thr); - } - } - - /* return iops if shutting down */ - fil_crypt_return_iops(&thr); - - /* release current space if shutting down */ - if (thr.space) { - fil_space_release(thr.space); - thr.space = NULL; - } - - mutex_enter(&fil_crypt_threads_mutex); - srv_n_fil_crypt_threads_started--; - os_event_set(fil_crypt_event); /* signal that we stopped */ - mutex_exit(&fil_crypt_threads_mutex); - - /* We count the number of threads in os_thread_exit(). A created - thread should always use that to exit and not use return() to exit. */ - - os_thread_exit(NULL); - - OS_THREAD_DUMMY_RETURN; -} - -/********************************************************************* -Adjust thread count for key rotation -@param[in] enw_cnt Number of threads to be used */ -UNIV_INTERN -void -fil_crypt_set_thread_cnt( - const uint new_cnt) -{ - if (!fil_crypt_threads_inited) { - fil_crypt_threads_init(); - } - - mutex_enter(&fil_crypt_threads_mutex); - - if (new_cnt > srv_n_fil_crypt_threads) { - uint add = new_cnt - srv_n_fil_crypt_threads; - srv_n_fil_crypt_threads = new_cnt; - for (uint i = 0; i < add; i++) { - os_thread_id_t rotation_thread_id; - os_thread_create(fil_crypt_thread, NULL, &rotation_thread_id); - - ib_logf(IB_LOG_LEVEL_INFO, - "Creating #%d thread id %lu total threads %u.", - i+1, os_thread_pf(rotation_thread_id), new_cnt); - } - } else if (new_cnt < srv_n_fil_crypt_threads) { - srv_n_fil_crypt_threads = new_cnt; - os_event_set(fil_crypt_threads_event); - } - - mutex_exit(&fil_crypt_threads_mutex); - - while(srv_n_fil_crypt_threads_started != srv_n_fil_crypt_threads) { - os_event_reset(fil_crypt_event); - os_event_wait_time(fil_crypt_event, 1000000); - } -} - -/********************************************************************* -Adjust max key age -@param[in] val New max key age */ -UNIV_INTERN -void -fil_crypt_set_rotate_key_age( - uint val) -{ - srv_fil_crypt_rotate_key_age = val; - os_event_set(fil_crypt_threads_event); -} - -/********************************************************************* -Adjust rotation iops -@param[in] val New max roation iops */ -UNIV_INTERN -void -fil_crypt_set_rotation_iops( - uint val) -{ - srv_n_fil_crypt_iops = val; - os_event_set(fil_crypt_threads_event); -} - -/********************************************************************* -Adjust encrypt tables -@param[in] val New setting for innodb-encrypt-tables */ -UNIV_INTERN -void -fil_crypt_set_encrypt_tables( - uint val) -{ - srv_encrypt_tables = val; - os_event_set(fil_crypt_threads_event); -} - -/********************************************************************* -Init threads for key rotation */ -UNIV_INTERN -void -fil_crypt_threads_init() -{ - ut_ad(mutex_own(&fil_system->mutex)); - if (!fil_crypt_threads_inited) { - fil_crypt_event = os_event_create(); - fil_crypt_threads_event = os_event_create(); - mutex_create(fil_crypt_threads_mutex_key, - &fil_crypt_threads_mutex, SYNC_NO_ORDER_CHECK); - - uint cnt = srv_n_fil_crypt_threads; - srv_n_fil_crypt_threads = 0; - fil_crypt_threads_inited = true; - fil_crypt_set_thread_cnt(cnt); - } -} - -/********************************************************************* -Clean up key rotation threads resources */ -UNIV_INTERN -void -fil_crypt_threads_cleanup() -{ - if (!fil_crypt_threads_inited) { - return; - } - ut_a(!srv_n_fil_crypt_threads_started); - os_event_free(fil_crypt_event); - fil_crypt_event = NULL; - os_event_free(fil_crypt_threads_event); - fil_crypt_threads_event = NULL; - mutex_free(&fil_crypt_threads_mutex); - fil_crypt_threads_inited = false; -} - -/********************************************************************* -Wait for crypt threads to stop accessing space -@param[in] space Tablespace */ -UNIV_INTERN -void -fil_space_crypt_close_tablespace( - const fil_space_t* space) -{ - if (!srv_encrypt_tables || !space->crypt_data) { - return; - } - - mutex_enter(&fil_crypt_threads_mutex); - - fil_space_crypt_t* crypt_data = space->crypt_data; - - time_t start = time(0); - time_t last = start; - - mutex_enter(&crypt_data->mutex); - mutex_exit(&fil_crypt_threads_mutex); - - uint cnt = crypt_data->rotate_state.active_threads; - bool flushing = crypt_data->rotate_state.flushing; - - while (cnt > 0 || flushing) { - mutex_exit(&crypt_data->mutex); - /* release dict mutex so that scrub threads can release their - * table references */ - dict_mutex_exit_for_mysql(); - - /* wakeup throttle (all) sleepers */ - os_event_set(fil_crypt_throttle_sleep_event); - - os_thread_sleep(20000); - dict_mutex_enter_for_mysql(); - mutex_enter(&crypt_data->mutex); - cnt = crypt_data->rotate_state.active_threads; - flushing = crypt_data->rotate_state.flushing; - - time_t now = time(0); - - if (now >= last + 30) { - ib_logf(IB_LOG_LEVEL_WARN, - "Waited %ld seconds to drop space: %s (" ULINTPF - ") active threads %u flushing=%d.", - now - start, space->name, space->id, cnt, flushing); - last = now; - } - } - - mutex_exit(&crypt_data->mutex); -} - -/********************************************************************* -Get crypt status for a space (used by information_schema) -@param[in] space Tablespace -@param[out] status Crypt status */ -UNIV_INTERN -void -fil_space_crypt_get_status( - const fil_space_t* space, - struct fil_space_crypt_status_t* status) -{ - memset(status, 0, sizeof(*status)); - - ut_ad(space->n_pending_ops > 0); - fil_crypt_read_crypt_data(const_cast<fil_space_t*>(space)); - status->space = space->id; - - if (fil_space_crypt_t* crypt_data = space->crypt_data) { - mutex_enter(&crypt_data->mutex); - status->scheme = crypt_data->type; - status->keyserver_requests = crypt_data->keyserver_requests; - status->min_key_version = crypt_data->min_key_version; - status->key_id = crypt_data->key_id; - - if (crypt_data->rotate_state.active_threads > 0 || - crypt_data->rotate_state.flushing) { - status->rotating = true; - status->flushing = - crypt_data->rotate_state.flushing; - status->rotate_next_page_number = - crypt_data->rotate_state.next_offset; - status->rotate_max_page_number = - crypt_data->rotate_state.max_offset; - } - - mutex_exit(&crypt_data->mutex); - - if (srv_encrypt_tables || crypt_data->min_key_version) { - status->current_key_version = - fil_crypt_get_latest_key_version(crypt_data); - } - } -} - -/********************************************************************* -Return crypt statistics -@param[out] stat Crypt statistics */ -UNIV_INTERN -void -fil_crypt_total_stat( - fil_crypt_stat_t *stat) -{ - mutex_enter(&crypt_stat_mutex); - *stat = crypt_stat; - mutex_exit(&crypt_stat_mutex); -} - -/********************************************************************* -Get scrub status for a space (used by information_schema) - -@param[in] space Tablespace -@param[out] status Scrub status */ -UNIV_INTERN -void -fil_space_get_scrub_status( - const fil_space_t* space, - struct fil_space_scrub_status_t* status) -{ - memset(status, 0, sizeof(*status)); - - ut_ad(space->n_pending_ops > 0); - fil_space_crypt_t* crypt_data = space->crypt_data; - - status->space = space->id; - - if (crypt_data != NULL) { - status->compressed = fsp_flags_get_zip_size(space->flags) > 0; - mutex_enter(&crypt_data->mutex); - status->last_scrub_completed = - crypt_data->rotate_state.scrubbing.last_scrub_completed; - if (crypt_data->rotate_state.active_threads > 0 && - crypt_data->rotate_state.scrubbing.is_active) { - status->scrubbing = true; - status->current_scrub_started = - crypt_data->rotate_state.start_time; - status->current_scrub_active_threads = - crypt_data->rotate_state.active_threads; - status->current_scrub_page_number = - crypt_data->rotate_state.next_offset; - status->current_scrub_max_page_number = - crypt_data->rotate_state.max_offset; - } - - mutex_exit(&crypt_data->mutex); - } -} |