diff options
Diffstat (limited to 'src/pmdk/src/libpmempool/check_btt_info.c')
-rw-r--r-- | src/pmdk/src/libpmempool/check_btt_info.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/src/pmdk/src/libpmempool/check_btt_info.c b/src/pmdk/src/libpmempool/check_btt_info.c new file mode 100644 index 000000000..2ef14e48f --- /dev/null +++ b/src/pmdk/src/libpmempool/check_btt_info.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright 2016-2018, Intel Corporation */ + +/* + * check_btt_info.c -- check BTT Info + */ + +#include <stdlib.h> +#include <stdint.h> +#include <endian.h> + +#include "out.h" +#include "util.h" +#include "btt.h" +#include "libpmempool.h" +#include "pmempool.h" +#include "pool.h" +#include "check_util.h" + +enum question { + Q_RESTORE_FROM_BACKUP, + Q_REGENERATE, + Q_REGENERATE_CHECKSUM, + Q_RESTORE_FROM_HEADER +}; + +/* + * location_release -- (internal) release check_btt_info_loc allocations + */ +static void +location_release(location *loc) +{ + free(loc->arenap); + loc->arenap = NULL; +} + +/* + * btt_info_checksum -- (internal) check BTT Info checksum + */ +static int +btt_info_checksum(PMEMpoolcheck *ppc, location *loc) +{ + LOG(3, NULL); + + loc->arenap = calloc(1, sizeof(struct arena)); + if (!loc->arenap) { + ERR("!calloc"); + ppc->result = CHECK_RESULT_INTERNAL_ERROR; + CHECK_ERR(ppc, "cannot allocate memory for arena"); + goto error_cleanup; + } + + /* read the BTT Info header at well known offset */ + if (pool_read(ppc->pool, &loc->arenap->btt_info, + sizeof(loc->arenap->btt_info), loc->offset)) { + CHECK_ERR(ppc, "arena %u: cannot read BTT Info header", + loc->arenap->id); + ppc->result = CHECK_RESULT_ERROR; + goto error_cleanup; + } + + loc->arenap->id = ppc->pool->narenas; + + /* BLK is consistent even without BTT Layout */ + if (ppc->pool->params.type == POOL_TYPE_BLK) { + int is_zeroed = util_is_zeroed((const void *) + &loc->arenap->btt_info, sizeof(loc->arenap->btt_info)); + if (is_zeroed) { + CHECK_INFO(ppc, "BTT Layout not written"); + loc->step = CHECK_STEP_COMPLETE; + ppc->pool->blk_no_layout = 1; + location_release(loc); + check_end(ppc->data); + return 0; + } + } + + /* check consistency of BTT Info */ + if (pool_btt_info_valid(&loc->arenap->btt_info)) { + CHECK_INFO(ppc, "arena %u: BTT Info header checksum correct", + loc->arenap->id); + loc->valid.btti_header = 1; + } else if (CHECK_IS_NOT(ppc, REPAIR)) { + CHECK_ERR(ppc, "arena %u: BTT Info header checksum incorrect", + loc->arenap->id); + ppc->result = CHECK_RESULT_NOT_CONSISTENT; + check_end(ppc->data); + goto error_cleanup; + } + + return 0; + +error_cleanup: + location_release(loc); + return -1; +} + +/* + * btt_info_backup -- (internal) check BTT Info backup + */ +static int +btt_info_backup(PMEMpoolcheck *ppc, location *loc) +{ + LOG(3, NULL); + + /* check BTT Info backup consistency */ + const size_t btt_info_size = sizeof(ppc->pool->bttc.btt_info); + uint64_t btt_info_off = pool_next_arena_offset(ppc->pool, loc->offset) - + btt_info_size; + + if (pool_read(ppc->pool, &ppc->pool->bttc.btt_info, btt_info_size, + btt_info_off)) { + CHECK_ERR(ppc, "arena %u: cannot read BTT Info backup", + loc->arenap->id); + goto error; + } + + /* check whether this BTT Info backup is valid */ + if (pool_btt_info_valid(&ppc->pool->bttc.btt_info)) { + loc->valid.btti_backup = 1; + + /* restore BTT Info from backup */ + if (!loc->valid.btti_header && CHECK_IS(ppc, REPAIR)) + CHECK_ASK(ppc, Q_RESTORE_FROM_BACKUP, "arena %u: BTT " + "Info header checksum incorrect.|Restore BTT " + "Info from backup?", loc->arenap->id); + } + + /* + * if BTT Info backup require repairs it will be fixed in further steps + */ + + return check_questions_sequence_validate(ppc); + +error: + ppc->result = CHECK_RESULT_ERROR; + location_release(loc); + return -1; +} + +/* + * btt_info_from_backup_fix -- (internal) fix BTT Info using its backup + */ +static int +btt_info_from_backup_fix(PMEMpoolcheck *ppc, location *loc, uint32_t question, + void *ctx) +{ + LOG(3, NULL); + + ASSERTeq(ctx, NULL); + ASSERTne(loc, NULL); + + switch (question) { + case Q_RESTORE_FROM_BACKUP: + CHECK_INFO(ppc, + "arena %u: restoring BTT Info header from backup", + loc->arenap->id); + + memcpy(&loc->arenap->btt_info, &ppc->pool->bttc.btt_info, + sizeof(loc->arenap->btt_info)); + loc->valid.btti_header = 1; + break; + default: + ERR("not implemented question id: %u", question); + } + + return 0; +} + +/* + * btt_info_gen -- (internal) ask whether try to regenerate BTT Info + */ +static int +btt_info_gen(PMEMpoolcheck *ppc, location *loc) +{ + LOG(3, NULL); + + if (loc->valid.btti_header) + return 0; + + ASSERT(CHECK_IS(ppc, REPAIR)); + + if (!loc->pool_valid.btti_offset) { + ppc->result = CHECK_RESULT_NOT_CONSISTENT; + check_end(ppc->data); + return CHECK_ERR(ppc, "can not find any valid BTT Info"); + } + + CHECK_ASK(ppc, Q_REGENERATE, + "arena %u: BTT Info header checksum incorrect.|Do you want to " + "regenerate BTT Info?", loc->arenap->id); + + return check_questions_sequence_validate(ppc); +} + +/* + * btt_info_gen_fix -- (internal) fix by regenerating BTT Info + */ +static int +btt_info_gen_fix(PMEMpoolcheck *ppc, location *loc, uint32_t question, + void *ctx) +{ + LOG(3, NULL); + + ASSERTeq(ctx, NULL); + ASSERTne(loc, NULL); + + switch (question) { + case Q_REGENERATE: + CHECK_INFO(ppc, "arena %u: regenerating BTT Info header", + loc->arenap->id); + + /* + * We do not have valid BTT Info backup so we get first valid + * BTT Info and try to calculate BTT Info for current arena + */ + uint64_t arena_size = ppc->pool->set_file->size - loc->offset; + if (arena_size > BTT_MAX_ARENA) + arena_size = BTT_MAX_ARENA; + + uint64_t space_left = ppc->pool->set_file->size - loc->offset - + arena_size; + + struct btt_info *bttd = &loc->arenap->btt_info; + struct btt_info *btts = &loc->pool_valid.btti; + + btt_info_convert2h(bttd); + + /* + * all valid BTT Info structures have the same signature, UUID, + * parent UUID, flags, major, minor, external LBA size, internal + * LBA size, nfree, info size and data offset + */ + memcpy(bttd->sig, btts->sig, BTTINFO_SIG_LEN); + memcpy(bttd->uuid, btts->uuid, BTTINFO_UUID_LEN); + memcpy(bttd->parent_uuid, btts->parent_uuid, BTTINFO_UUID_LEN); + memset(bttd->unused, 0, BTTINFO_UNUSED_LEN); + bttd->flags = btts->flags; + bttd->major = btts->major; + bttd->minor = btts->minor; + + /* other parameters can be calculated */ + if (btt_info_set(bttd, btts->external_lbasize, btts->nfree, + arena_size, space_left)) { + CHECK_ERR(ppc, "can not restore BTT Info"); + return -1; + } + + ASSERTeq(bttd->external_lbasize, btts->external_lbasize); + ASSERTeq(bttd->internal_lbasize, btts->internal_lbasize); + ASSERTeq(bttd->nfree, btts->nfree); + ASSERTeq(bttd->infosize, btts->infosize); + ASSERTeq(bttd->dataoff, btts->dataoff); + return 0; + + default: + ERR("not implemented question id: %u", question); + return -1; + } +} + +/* + * btt_info_checksum_retry -- (internal) check BTT Info checksum + */ +static int +btt_info_checksum_retry(PMEMpoolcheck *ppc, location *loc) +{ + LOG(3, NULL); + + if (loc->valid.btti_header) + return 0; + + btt_info_convert2le(&loc->arenap->btt_info); + + /* check consistency of BTT Info */ + if (pool_btt_info_valid(&loc->arenap->btt_info)) { + CHECK_INFO(ppc, "arena %u: BTT Info header checksum correct", + loc->arenap->id); + loc->valid.btti_header = 1; + return 0; + } + + if (CHECK_IS_NOT(ppc, ADVANCED)) { + ppc->result = CHECK_RESULT_CANNOT_REPAIR; + CHECK_INFO(ppc, REQUIRE_ADVANCED); + CHECK_ERR(ppc, "arena %u: BTT Info header checksum incorrect", + loc->arenap->id); + check_end(ppc->data); + goto error_cleanup; + } + + CHECK_ASK(ppc, Q_REGENERATE_CHECKSUM, + "arena %u: BTT Info header checksum incorrect.|Do you want to " + "regenerate BTT Info checksum?", loc->arenap->id); + + return check_questions_sequence_validate(ppc); + +error_cleanup: + location_release(loc); + return -1; +} + +/* + * btt_info_checksum_fix -- (internal) fix by regenerating BTT Info checksum + */ +static int +btt_info_checksum_fix(PMEMpoolcheck *ppc, location *loc, uint32_t question, + void *ctx) +{ + LOG(3, NULL); + + ASSERTeq(ctx, NULL); + ASSERTne(loc, NULL); + + switch (question) { + case Q_REGENERATE_CHECKSUM: + util_checksum(&loc->arenap->btt_info, sizeof(struct btt_info), + &loc->arenap->btt_info.checksum, 1, 0); + loc->valid.btti_header = 1; + break; + + default: + ERR("not implemented question id: %u", question); + } + + return 0; +} + +/* + * btt_info_backup_checksum -- (internal) check BTT Info backup checksum + */ +static int +btt_info_backup_checksum(PMEMpoolcheck *ppc, location *loc) +{ + LOG(3, NULL); + + ASSERT(loc->valid.btti_header); + + if (loc->valid.btti_backup) + return 0; + + /* BTT Info backup is not valid so it must be fixed */ + if (CHECK_IS_NOT(ppc, REPAIR)) { + CHECK_ERR(ppc, + "arena %u: BTT Info backup checksum incorrect", + loc->arenap->id); + ppc->result = CHECK_RESULT_NOT_CONSISTENT; + check_end(ppc->data); + goto error_cleanup; + } + + CHECK_ASK(ppc, Q_RESTORE_FROM_HEADER, + "arena %u: BTT Info backup checksum incorrect.|Do you want to " + "restore it from BTT Info header?", loc->arenap->id); + + return check_questions_sequence_validate(ppc); + +error_cleanup: + location_release(loc); + return -1; +} + +/* + * btt_info_backup_fix -- (internal) prepare restore BTT Info backup from header + */ +static int +btt_info_backup_fix(PMEMpoolcheck *ppc, location *loc, uint32_t question, + void *ctx) +{ + LOG(3, NULL); + + ASSERTeq(ctx, NULL); + ASSERTne(loc, NULL); + + switch (question) { + case Q_RESTORE_FROM_HEADER: + /* BTT Info backup would be restored in check_write step */ + CHECK_INFO(ppc, + "arena %u: restoring BTT Info backup from header", + loc->arenap->id); + break; + + default: + ERR("not implemented question id: %u", question); + } + + return 0; +} + +struct step { + int (*check)(PMEMpoolcheck *, location *); + int (*fix)(PMEMpoolcheck *, location *, uint32_t, void *); +}; + +static const struct step steps[] = { + { + .check = btt_info_checksum, + }, + { + .check = btt_info_backup, + }, + { + .fix = btt_info_from_backup_fix, + }, + { + .check = btt_info_gen, + }, + { + .fix = btt_info_gen_fix, + }, + { + .check = btt_info_checksum_retry, + }, + { + .fix = btt_info_checksum_fix, + }, + { + .check = btt_info_backup_checksum, + }, + { + .fix = btt_info_backup_fix, + }, + { + .check = NULL, + .fix = NULL, + }, +}; + +/* + * step_exe -- (internal) perform single step according to its parameters + */ +static inline int +step_exe(PMEMpoolcheck *ppc, location *loc) +{ + ASSERT(loc->step < ARRAY_SIZE(steps)); + + const struct step *step = &steps[loc->step++]; + + if (!step->fix) + return step->check(ppc, loc); + + if (!check_answer_loop(ppc, loc, NULL, 1, step->fix)) + return 0; + + if (check_has_error(ppc->data)) + location_release(loc); + + return -1; +} + +/* + * check_btt_info -- entry point for btt info check + */ +void +check_btt_info(PMEMpoolcheck *ppc) +{ + LOG(3, NULL); + + location *loc = check_get_step_data(ppc->data); + uint64_t nextoff = 0; + + /* initialize check */ + if (!loc->offset) { + CHECK_INFO(ppc, "checking BTT Info headers"); + loc->offset = sizeof(struct pool_hdr); + if (ppc->pool->params.type == POOL_TYPE_BLK) + loc->offset += ALIGN_UP(sizeof(struct pmemblk) - + sizeof(struct pool_hdr), + BLK_FORMAT_DATA_ALIGN); + + loc->pool_valid.btti_offset = pool_get_first_valid_btt( + ppc->pool, &loc->pool_valid.btti, loc->offset, NULL); + + /* Without valid BTT Info we can not proceed */ + if (!loc->pool_valid.btti_offset) { + if (ppc->pool->params.type == POOL_TYPE_BTT) { + CHECK_ERR(ppc, + "can not find any valid BTT Info"); + ppc->result = CHECK_RESULT_NOT_CONSISTENT; + check_end(ppc->data); + return; + } + } else + btt_info_convert2h(&loc->pool_valid.btti); + } + + do { + /* jump to next offset */ + if (ppc->result != CHECK_RESULT_PROCESS_ANSWERS) { + loc->offset += nextoff; + loc->step = 0; + loc->valid.btti_header = 0; + loc->valid.btti_backup = 0; + } + + /* do all checks */ + while (CHECK_NOT_COMPLETE(loc, steps)) { + if (step_exe(ppc, loc) || ppc->pool->blk_no_layout == 1) + return; + } + + /* save offset and insert BTT to cache for next steps */ + loc->arenap->offset = loc->offset; + loc->arenap->valid = true; + check_insert_arena(ppc, loc->arenap); + nextoff = le64toh(loc->arenap->btt_info.nextoff); + + } while (nextoff > 0); +} |