Commit def48e62 authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-8141 InnoDB: background encryption thread uses FIL_DEFAULT_ENCRYPTION_KEY

* check key version per key id (that is, per tablespace).
* wake encryption thread when a tablespace needs re-encryption
parent 6e4c22af
......@@ -104,6 +104,13 @@ UNIV_INTERN mysql_pfs_key_t fil_crypt_stat_mutex_key;
UNIV_INTERN mysql_pfs_key_t fil_crypt_data_mutex_key;
#endif
static bool
fil_crypt_needs_rotation(
/*=====================*/
uint key_version, /*!< in: Key version */
uint latest_key_version, /*!< in: Latest key version */
uint rotate_key_age); /*!< in: When to rotate */
/**
* Magic pattern in start of crypt data on page 0
*/
......@@ -218,6 +225,24 @@ fil_crypt_get_key(
mutex_exit(&crypt_data->mutex);
}
/******************************************************************
Get the latest(key-version), waking the encrypt thread, if needed */
static inline
uint
fil_crypt_get_latest_key_version(
/*=====================*/
fil_space_crypt_t* crypt_data) /*!< in: crypt data */
{
uint rc = encryption_key_get_latest_version(crypt_data->key_id);
if (fil_crypt_needs_rotation(crypt_data->min_key_version,
rc, srv_fil_crypt_rotate_key_age)) {
os_event_set(fil_crypt_threads_event);
}
return rc;
}
/******************************************************************
Get key bytes for a space/latest(key-version) */
static inline
......@@ -230,7 +255,7 @@ fil_crypt_get_latest_key(
uint* version) /*!< in: Key version */
{
// used for key rotation - get the next key id from the key provider
uint rc = *version = encryption_key_get_latest_version(crypt_data->key_id);
uint rc = *version = fil_crypt_get_latest_key_version(crypt_data);
if (rc == ENCRYPTION_KEY_VERSION_INVALID) {
ib_logf(IB_LOG_LEVEL_FATAL,
......@@ -964,12 +989,13 @@ fil_space_verify_crypt_checksum(
/** A copy of global key state */
struct key_state_t {
key_state_t() : key_version(0),
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;
};
......@@ -983,7 +1009,7 @@ fil_crypt_get_key_state(
{
if (srv_encrypt_tables) {
new_state->key_version =
encryption_key_get_latest_version(FIL_DEFAULT_ENCRYPTION_KEY);
encryption_key_get_latest_version(new_state->key_id);
new_state->rotate_key_age = srv_fil_crypt_rotate_key_age;
ut_a(new_state->key_version != ENCRYPTION_KEY_VERSION_INVALID);
ut_a(new_state->key_version != ENCRYPTION_KEY_NOT_ENCRYPTED);
......@@ -999,25 +1025,27 @@ Check if a key needs rotation given a key_state
static bool
fil_crypt_needs_rotation(
/*=====================*/
uint key_version, /*!< in: Key version */
const key_state_t* key_state) /*!< in: Key state */
uint key_version, /*!< in: Key version */
uint latest_key_version, /*!< in: Latest key version */
uint rotate_key_age) /*!< in: When to rotate */
{
// TODO(jonaso): Add support for rotating encrypted => unencrypted
if (key_version == ENCRYPTION_KEY_VERSION_INVALID)
return false;
if (key_version == 0 && key_state->key_version != 0) {
if (key_version == 0 && latest_key_version != 0) {
/* this is rotation unencrypted => encrypted
* ignore rotate_key_age */
return true;
}
if (key_state->key_version == 0 && key_version != 0) {
if (latest_key_version == 0 && key_version != 0) {
/* this is rotation encrypted => unencrypted */
return true;
}
/* this is rotation encrypted => encrypted,
* only reencrypt if key is sufficiently old */
if (key_version + key_state->rotate_key_age < key_state->key_version) {
if (key_version + rotate_key_age < latest_key_version) {
return true;
}
......@@ -1216,7 +1244,7 @@ static
bool
fil_crypt_space_needs_rotation(
uint space, /*!< in: FIL space id */
const key_state_t* key_state, /*!< in: Key state */
key_state_t* key_state, /*!< in: Key state */
bool* recheck) /*!< out: needs recheck ? */
{
if (fil_space_get_type(space) != FIL_TABLESPACE) {
......@@ -1274,8 +1302,14 @@ fil_crypt_space_needs_rotation(
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);
}
bool need_key_rotation = fil_crypt_needs_rotation(
crypt_data->min_key_version, key_state);
crypt_data->min_key_version,
key_state->key_version, key_state->rotate_key_age);
time_t diff = time(0) - crypt_data->rotate_state.scrubbing.
last_scrub_completed;
......@@ -1522,7 +1556,7 @@ UNIV_INTERN
bool
fil_crypt_find_space_to_rotate(
/*===========================*/
const key_state_t* key_state, /*!< in: Key state */
key_state_t* key_state, /*!< in: Key state */
rotate_thread_t* state, /*!< in: Key rotation state */
bool* recheck) /*!< out: true if recheck
needed */
......@@ -1547,6 +1581,7 @@ fil_crypt_find_space_to_rotate(
ulint space = state->space;
if (fil_crypt_space_needs_rotation(space, 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;
......@@ -1576,6 +1611,7 @@ fil_crypt_start_rotate_space(
fil_space_crypt_t *crypt_data = fil_space_get_crypt_data(space);
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 */
......@@ -1621,6 +1657,7 @@ fil_crypt_find_page_to_rotate(
fil_space_crypt_t *crypt_data = fil_space_get_crypt_data(space);
mutex_enter(&crypt_data->mutex);
ut_ad(key_state->key_id == crypt_data->key_id);
if (crypt_data->closing == false &&
crypt_data->rotate_state.next_offset <
......@@ -1842,7 +1879,8 @@ fil_crypt_rotate_page(
if (kv == 0 &&
fil_crypt_is_page_uninitialized(frame, zip_size)) {
;
} else if (fil_crypt_needs_rotation(kv, key_state)) {
} else if (fil_crypt_needs_rotation(kv, key_state->key_version,
key_state->rotate_key_age)) {
/* page can be "fresh" i.e never written in case
* kv == 0 or it should have a key version at least
......@@ -2144,30 +2182,21 @@ DECLARE_THREAD(fil_crypt_thread)(
/* if we find a space that is starting, skip over it and recheck it later */
bool recheck = false;
key_state_t key_state;
fil_crypt_get_key_state(&key_state);
/* make sure that thread always checks all tablespace when starting.
*
* by decreasing key_version, loop that waits for change in key-state
* should exit immediately causing thread to check all spaces when starting */
key_state.key_version--;
while (!thr.should_shutdown()) {
key_state_t new_state;
fil_crypt_get_key_state(&new_state);
time_t wait_start = time(0);
while (!thr.should_shutdown() && key_state == new_state) {
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);
os_event_wait_time(fil_crypt_threads_event, 1000000);
fil_crypt_get_key_state(&new_state);
if (os_event_wait_time(fil_crypt_threads_event, 1000000) == 0) {
break;
}
if (recheck) {
/* check recheck here, after sleep, so
......@@ -2185,7 +2214,6 @@ DECLARE_THREAD(fil_crypt_thread)(
recheck = false;
thr.first = true; // restart from first tablespace
key_state = new_state; // save for next loop
/* iterate all spaces searching for those needing rotation */
while (!thr.should_shutdown() &&
......@@ -2215,8 +2243,8 @@ DECLARE_THREAD(fil_crypt_thread)(
/* complete rotation */
fil_crypt_complete_rotate_space(&new_state, &thr);
/* refresh key state */
fil_crypt_get_key_state(&new_state);
/* force key state refresh */
new_state.key_id= 0;
/* return iops */
fil_crypt_return_iops(&thr);
......@@ -2448,14 +2476,17 @@ fil_space_crypt_get_status(
}
mutex_exit(&crypt_data->mutex);
if (srv_encrypt_tables) {
if (srv_encrypt_tables || crypt_data->min_key_version) {
status->current_key_version =
encryption_key_get_latest_version(crypt_data->key_id);
fil_crypt_get_latest_key_version(crypt_data);
} else {
status->current_key_version = 0;
}
} else {
memset(status, 0, sizeof(*status));
if (srv_encrypt_tables) {
os_event_set(fil_crypt_threads_event);
}
}
return crypt_data == NULL ? 1 : 0;
......
......@@ -104,6 +104,13 @@ UNIV_INTERN mysql_pfs_key_t fil_crypt_stat_mutex_key;
UNIV_INTERN mysql_pfs_key_t fil_crypt_data_mutex_key;
#endif
static bool
fil_crypt_needs_rotation(
/*=====================*/
uint key_version, /*!< in: Key version */
uint latest_key_version, /*!< in: Latest key version */
uint rotate_key_age); /*!< in: When to rotate */
/**
* Magic pattern in start of crypt data on page 0
*/
......@@ -218,6 +225,24 @@ fil_crypt_get_key(
mutex_exit(&crypt_data->mutex);
}
/******************************************************************
Get the latest(key-version), waking the encrypt thread, if needed */
static inline
uint
fil_crypt_get_latest_key_version(
/*=====================*/
fil_space_crypt_t* crypt_data) /*!< in: crypt data */
{
uint rc = encryption_key_get_latest_version(crypt_data->key_id);
if (fil_crypt_needs_rotation(crypt_data->min_key_version,
rc, srv_fil_crypt_rotate_key_age)) {
os_event_set(fil_crypt_threads_event);
}
return rc;
}
/******************************************************************
Get key bytes for a space/latest(key-version) */
static inline
......@@ -230,7 +255,7 @@ fil_crypt_get_latest_key(
uint* version) /*!< in: Key version */
{
// used for key rotation - get the next key id from the key provider
uint rc = *version = encryption_key_get_latest_version(crypt_data->key_id);
uint rc = *version = fil_crypt_get_latest_key_version(crypt_data);
if (rc == ENCRYPTION_KEY_VERSION_INVALID) {
ib_logf(IB_LOG_LEVEL_FATAL,
......@@ -964,12 +989,13 @@ fil_space_verify_crypt_checksum(
/** A copy of global key state */
struct key_state_t {
key_state_t() : key_version(0),
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;
};
......@@ -983,7 +1009,7 @@ fil_crypt_get_key_state(
{
if (srv_encrypt_tables) {
new_state->key_version =
encryption_key_get_latest_version(FIL_DEFAULT_ENCRYPTION_KEY);
encryption_key_get_latest_version(new_state->key_id);
new_state->rotate_key_age = srv_fil_crypt_rotate_key_age;
ut_a(new_state->key_version != ENCRYPTION_KEY_VERSION_INVALID);
ut_a(new_state->key_version != ENCRYPTION_KEY_NOT_ENCRYPTED);
......@@ -999,25 +1025,27 @@ Check if a key needs rotation given a key_state
static bool
fil_crypt_needs_rotation(
/*=====================*/
uint key_version, /*!< in: Key version */
const key_state_t* key_state) /*!< in: Key state */
uint key_version, /*!< in: Key version */
uint latest_key_version, /*!< in: Latest key version */
uint rotate_key_age) /*!< in: When to rotate */
{
// TODO(jonaso): Add support for rotating encrypted => unencrypted
if (key_version == ENCRYPTION_KEY_VERSION_INVALID)
return false;
if (key_version == 0 && key_state->key_version != 0) {
if (key_version == 0 && latest_key_version != 0) {
/* this is rotation unencrypted => encrypted
* ignore rotate_key_age */
return true;
}
if (key_state->key_version == 0 && key_version != 0) {
if (latest_key_version == 0 && key_version != 0) {
/* this is rotation encrypted => unencrypted */
return true;
}
/* this is rotation encrypted => encrypted,
* only reencrypt if key is sufficiently old */
if (key_version + key_state->rotate_key_age < key_state->key_version) {
if (key_version + rotate_key_age < latest_key_version) {
return true;
}
......@@ -1216,7 +1244,7 @@ static
bool
fil_crypt_space_needs_rotation(
uint space, /*!< in: FIL space id */
const key_state_t* key_state, /*!< in: Key state */
key_state_t* key_state, /*!< in: Key state */
bool* recheck) /*!< out: needs recheck ? */
{
if (fil_space_get_type(space) != FIL_TABLESPACE) {
......@@ -1274,8 +1302,14 @@ fil_crypt_space_needs_rotation(
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);
}
bool need_key_rotation = fil_crypt_needs_rotation(
crypt_data->min_key_version, key_state);
crypt_data->min_key_version,
key_state->key_version, key_state->rotate_key_age);
time_t diff = time(0) - crypt_data->rotate_state.scrubbing.
last_scrub_completed;
......@@ -1522,7 +1556,7 @@ UNIV_INTERN
bool
fil_crypt_find_space_to_rotate(
/*===========================*/
const key_state_t* key_state, /*!< in: Key state */
key_state_t* key_state, /*!< in: Key state */
rotate_thread_t* state, /*!< in: Key rotation state */
bool* recheck) /*!< out: true if recheck
needed */
......@@ -1547,6 +1581,7 @@ fil_crypt_find_space_to_rotate(
ulint space = state->space;
if (fil_crypt_space_needs_rotation(space, 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;
......@@ -1576,6 +1611,7 @@ fil_crypt_start_rotate_space(
fil_space_crypt_t *crypt_data = fil_space_get_crypt_data(space);
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 */
......@@ -1621,6 +1657,7 @@ fil_crypt_find_page_to_rotate(
fil_space_crypt_t *crypt_data = fil_space_get_crypt_data(space);
mutex_enter(&crypt_data->mutex);
ut_ad(key_state->key_id == crypt_data->key_id);
if (crypt_data->closing == false &&
crypt_data->rotate_state.next_offset <
......@@ -1842,7 +1879,8 @@ fil_crypt_rotate_page(
if (kv == 0 &&
fil_crypt_is_page_uninitialized(frame, zip_size)) {
;
} else if (fil_crypt_needs_rotation(kv, key_state)) {
} else if (fil_crypt_needs_rotation(kv, key_state->key_version,
key_state->rotate_key_age)) {
/* page can be "fresh" i.e never written in case
* kv == 0 or it should have a key version at least
......@@ -2144,30 +2182,21 @@ DECLARE_THREAD(fil_crypt_thread)(
/* if we find a space that is starting, skip over it and recheck it later */
bool recheck = false;
key_state_t key_state;
fil_crypt_get_key_state(&key_state);
/* make sure that thread always checks all tablespace when starting.
*
* by decreasing key_version, loop that waits for change in key-state
* should exit immediately causing thread to check all spaces when starting */
key_state.key_version--;
while (!thr.should_shutdown()) {
key_state_t new_state;
fil_crypt_get_key_state(&new_state);
time_t wait_start = time(0);
while (!thr.should_shutdown() && key_state == new_state) {
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);
os_event_wait_time(fil_crypt_threads_event, 1000000);
fil_crypt_get_key_state(&new_state);
if (os_event_wait_time(fil_crypt_threads_event, 1000000) == 0) {
break;
}
if (recheck) {
/* check recheck here, after sleep, so
......@@ -2185,7 +2214,6 @@ DECLARE_THREAD(fil_crypt_thread)(
recheck = false;
thr.first = true; // restart from first tablespace
key_state = new_state; // save for next loop
/* iterate all spaces searching for those needing rotation */
while (!thr.should_shutdown() &&
......@@ -2215,8 +2243,8 @@ DECLARE_THREAD(fil_crypt_thread)(
/* complete rotation */
fil_crypt_complete_rotate_space(&new_state, &thr);
/* refresh key state */
fil_crypt_get_key_state(&new_state);
/* force key state refresh */
new_state.key_id= 0;
/* return iops */
fil_crypt_return_iops(&thr);
......@@ -2448,14 +2476,17 @@ fil_space_crypt_get_status(
}
mutex_exit(&crypt_data->mutex);
if (srv_encrypt_tables) {
if (srv_encrypt_tables || crypt_data->min_key_version) {
status->current_key_version =
encryption_key_get_latest_version(crypt_data->key_id);
fil_crypt_get_latest_key_version(crypt_data);
} else {
status->current_key_version = 0;
}
} else {
memset(status, 0, sizeof(*status));
if (srv_encrypt_tables) {
os_event_set(fil_crypt_threads_event);
}
}
return crypt_data == NULL ? 1 : 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment