#include <stdint.h>
#include <sys/param.h>
#include <endian.h>
#include "out.h"
#include "btt.h"
#include "libpmempool.h"
#include "pmempool.h"
#include "pool.h"
#include "check_util.h"
enum questions {
Q_REPAIR_MAP,
Q_REPAIR_FLOG,
};
static int
flog_read(PMEMpoolcheck *ppc, struct arena *arenap)
{
uint64_t flogoff = arenap->offset + arenap->btt_info.flogoff;
arenap->flogsize = btt_flog_size(arenap->btt_info.nfree);
arenap->flog = malloc(arenap->flogsize);
if (!arenap->flog) {
ERR("!malloc");
goto error_malloc;
}
if (pool_read(ppc->pool, arenap->flog, arenap->flogsize, flogoff))
goto error_read;
uint8_t *ptr = arenap->flog;
uint32_t i;
for (i = 0; i < arenap->btt_info.nfree; i++) {
struct btt_flog *flog = (struct btt_flog *)ptr;
btt_flog_convert2h(&flog[0]);
btt_flog_convert2h(&flog[1]);
ptr += BTT_FLOG_PAIR_ALIGN;
}
return 0;
error_read:
free(arenap->flog);
arenap->flog = NULL;
error_malloc:
return -1;
}
static int
map_read(PMEMpoolcheck *ppc, struct arena *arenap)
{
uint64_t mapoff = arenap->offset + arenap->btt_info.mapoff;
arenap->mapsize = btt_map_size(arenap->btt_info.external_nlba);
ASSERT(arenap->mapsize != 0);
arenap->map = malloc(arenap->mapsize);
if (!arenap->map) {
ERR("!malloc");
goto error_malloc;
}
if (pool_read(ppc->pool, arenap->map, arenap->mapsize, mapoff)) {
goto error_read;
}
uint32_t i;
for (i = 0; i < arenap->btt_info.external_nlba; i++)
arenap->map[i] = le32toh(arenap->map[i]);
return 0;
error_read:
free(arenap->map);
arenap->map = NULL;
error_malloc:
return -1;
}
struct list_item {
LIST_ENTRY(list_item) next;
uint32_t val;
};
struct list {
LIST_HEAD(listhead, list_item) head;
uint32_t count;
};
static struct list *
list_alloc(void)
{
struct list *list = malloc(sizeof(struct list));
if (!list) {
ERR("!malloc");
return NULL;
}
LIST_INIT(&list->head);
list->count = 0;
return list;
}
static struct list_item *
list_push(struct list *list, uint32_t val)
{
struct list_item *item = malloc(sizeof(*item));
if (!item) {
ERR("!malloc");
return NULL;
}
item->val = val;
list->count++;
LIST_INSERT_HEAD(&list->head, item, next);
return item;
}
static int
list_pop(struct list *list, uint32_t *valp)
{
if (!LIST_EMPTY(&list->head)) {
struct list_item *i = LIST_FIRST(&list->head);
LIST_REMOVE(i, next);
if (valp)
*valp = i->val;
free(i);
list->count--;
return 1;
}
return 0;
}
static void
list_free(struct list *list)
{
while (list_pop(list, NULL))
;
free(list);
}
static int
cleanup(PMEMpoolcheck *ppc, location *loc)
{
LOG(3, NULL);
if (loc->list_unmap)
list_free(loc->list_unmap);
if (loc->list_flog_inval)
list_free(loc->list_flog_inval);
if (loc->list_inval)
list_free(loc->list_inval);
if (loc->fbitmap)
free(loc->fbitmap);
if (loc->bitmap)
free(loc->bitmap);
if (loc->dup_bitmap)
free(loc->dup_bitmap);
return 0;
}
static int
init(PMEMpoolcheck *ppc, location *loc)
{
LOG(3, NULL);
struct arena *arenap = loc->arenap;
if (flog_read(ppc, arenap)) {
CHECK_ERR(ppc, "arena %u: cannot read BTT Flog", arenap->id);
goto error;
}
if (map_read(ppc, arenap)) {
CHECK_ERR(ppc, "arena %u: cannot read BTT Map", arenap->id);
goto error;
}
uint32_t bitmapsize = howmany(arenap->btt_info.internal_nlba, 8);
loc->bitmap = calloc(bitmapsize, 1);
if (!loc->bitmap) {
ERR("!calloc");
CHECK_ERR(ppc, "arena %u: cannot allocate memory for blocks "
"bitmap", arenap->id);
goto error;
}
loc->dup_bitmap = calloc(bitmapsize, 1);
if (!loc->dup_bitmap) {
ERR("!calloc");
CHECK_ERR(ppc, "arena %u: cannot allocate memory for "
"duplicated blocks bitmap", arenap->id);
goto error;
}
loc->fbitmap = calloc(bitmapsize, 1);
if (!loc->fbitmap) {
ERR("!calloc");
CHECK_ERR(ppc, "arena %u: cannot allocate memory for BTT Flog "
"bitmap", arenap->id);
goto error;
}
loc->list_inval = list_alloc();
if (!loc->list_inval) {
CHECK_ERR(ppc,
"arena %u: cannot allocate memory for invalid BTT map "
"entries list", arenap->id);
goto error;
}
loc->list_flog_inval = list_alloc();
if (!loc->list_flog_inval) {
CHECK_ERR(ppc,
"arena %u: cannot allocate memory for invalid BTT Flog "
"entries list", arenap->id);
goto error;
}
loc->list_unmap = list_alloc();
if (!loc->list_unmap) {
CHECK_ERR(ppc,
"arena %u: cannot allocate memory for unmaped blocks "
"list", arenap->id);
goto error;
}
return 0;
error:
ppc->result = CHECK_RESULT_ERROR;
cleanup(ppc, loc);
return -1;
}
static inline uint32_t
map_get_postmap_lba(struct arena *arenap, uint32_t i)
{
uint32_t entry = arenap->map[i];
if (map_entry_is_initial(entry))
return i;
return entry & BTT_MAP_ENTRY_LBA_MASK;
}
static int
map_entry_check(PMEMpoolcheck *ppc, location *loc, uint32_t i)
{
struct arena *arenap = loc->arenap;
uint32_t lba = map_get_postmap_lba(arenap, i);
if (lba < arenap->btt_info.internal_nlba) {
if (util_isset(loc->bitmap, lba)) {
CHECK_INFO(ppc, "arena %u: BTT Map entry %u duplicated "
"at %u", arenap->id, lba, i);
util_setbit(loc->dup_bitmap, lba);
if (!list_push(loc->list_inval, i))
return -1;
} else
util_setbit(loc->bitmap, lba);
} else {
CHECK_INFO(ppc, "arena %u: invalid BTT Map entry at %u",
arenap->id, i);
if (!list_push(loc->list_inval, i))
return -1;
}
return 0;
}
static int
flog_entry_check(PMEMpoolcheck *ppc, location *loc, uint32_t i,
uint8_t **ptr)
{
struct arena *arenap = loc->arenap;
struct btt_flog *flog = (struct btt_flog *)*ptr;
int next;
struct btt_flog *flog_cur = btt_flog_get_valid(flog, &next);
if (!flog_cur) {
CHECK_INFO(ppc, "arena %u: invalid BTT Flog entry at %u",
arenap->id, i);
if (!list_push(loc->list_flog_inval, i))
return -1;
goto next;
}
uint32_t entry = flog_cur->old_map & BTT_MAP_ENTRY_LBA_MASK;
uint32_t new_entry = flog_cur->new_map & BTT_MAP_ENTRY_LBA_MASK;
if (flog_cur->lba >= arenap->btt_info.external_nlba ||
entry >= arenap->btt_info.internal_nlba ||
new_entry >= arenap->btt_info.internal_nlba) {
CHECK_INFO(ppc, "arena %u: invalid BTT Flog entry at %u",
arenap->id, i);
if (!list_push(loc->list_flog_inval, i))
return -1;
goto next;
}
if (util_isset(loc->fbitmap, entry)) {
CHECK_INFO(ppc, "arena %u: duplicated BTT Flog entry at %u\n",
arenap->id, i);
if (!list_push(loc->list_flog_inval, i))
return -1;
} else if (util_isset(loc->bitmap, entry)) {
if (util_isset(loc->bitmap, new_entry)) {
CHECK_INFO(ppc, "arena %u: duplicated BTT Flog entry "
"at %u", arenap->id, i);
util_setbit(loc->dup_bitmap, new_entry);
if (!list_push(loc->list_flog_inval, i))
return -1;
} else {
util_setbit(loc->bitmap, new_entry);
util_setbit(loc->fbitmap, entry);
}
} else {
int flog_valid = 1;
if (entry == new_entry)
flog_valid = (next == 1) && (flog_cur->seq == 1) &&
util_is_zeroed((const void *)&flog[1],
sizeof(flog[1]));
if (flog_valid) {
util_setbit(loc->bitmap, entry);
util_setbit(loc->fbitmap, entry);
} else {
CHECK_INFO(ppc, "arena %u: invalid BTT Flog entry at "
"%u", arenap->id, i);
if (!list_push(loc->list_flog_inval, i))
return -1;
}
}
next:
*ptr += BTT_FLOG_PAIR_ALIGN;
return 0;
}
static int
arena_map_flog_check(PMEMpoolcheck *ppc, location *loc)
{
LOG(3, NULL);
struct arena *arenap = loc->arenap;
uint32_t i;
for (i = 0; i < arenap->btt_info.external_nlba; i++) {
if (map_entry_check(ppc, loc, i))
goto error_push;
}
uint8_t *ptr = arenap->flog;
for (i = 0; i < arenap->btt_info.nfree; i++) {
if (flog_entry_check(ppc, loc, i, &ptr))
goto error_push;
}
for (i = 0; i < arenap->btt_info.internal_nlba; i++) {
if (!util_isset(loc->bitmap, i)) {
CHECK_INFO(ppc, "arena %u: unmapped block %u",
arenap->id, i);
if (!list_push(loc->list_unmap, i))
goto error_push;
}
}
if (loc->list_unmap->count)
CHECK_INFO(ppc, "arena %u: number of unmapped blocks: %u",
arenap->id, loc->list_unmap->count);
if (loc->list_inval->count)
CHECK_INFO(ppc, "arena %u: number of invalid BTT Map entries: "
"%u", arenap->id, loc->list_inval->count);
if (loc->list_flog_inval->count)
CHECK_INFO(ppc, "arena %u: number of invalid BTT Flog entries: "
"%u", arenap->id, loc->list_flog_inval->count);
if (CHECK_IS_NOT(ppc, REPAIR) && loc->list_unmap->count > 0) {
ppc->result = CHECK_RESULT_NOT_CONSISTENT;
check_end(ppc->data);
goto cleanup;
}
if (loc->list_unmap->count != (loc->list_inval->count +
loc->list_flog_inval->count)) {
ppc->result = CHECK_RESULT_CANNOT_REPAIR;
CHECK_ERR(ppc, "arena %u: cannot repair BTT Map and Flog",
arenap->id);
goto cleanup;
}
if (CHECK_IS_NOT(ppc, ADVANCED) && loc->list_inval->count +
loc->list_flog_inval->count > 0) {
ppc->result = CHECK_RESULT_CANNOT_REPAIR;
CHECK_INFO(ppc, REQUIRE_ADVANCED);
CHECK_ERR(ppc, "BTT Map and / or BTT Flog contain invalid "
"entries");
check_end(ppc->data);
goto cleanup;
}
if (loc->list_inval->count > 0) {
CHECK_ASK(ppc, Q_REPAIR_MAP, "Do you want to repair invalid "
"BTT Map entries?");
}
if (loc->list_flog_inval->count > 0) {
CHECK_ASK(ppc, Q_REPAIR_FLOG, "Do you want to repair invalid "
"BTT Flog entries?");
}
return check_questions_sequence_validate(ppc);
error_push:
CHECK_ERR(ppc, "arena %u: cannot allocate momory for list item",
arenap->id);
ppc->result = CHECK_RESULT_ERROR;
cleanup:
cleanup(ppc, loc);
return -1;
}
static int
arena_map_flog_fix(PMEMpoolcheck *ppc, location *loc, uint32_t question,
void *ctx)
{
LOG(3, NULL);
ASSERTeq(ctx, NULL);
ASSERTne(loc, NULL);
struct arena *arenap = loc->arenap;
uint32_t inval;
uint32_t unmap;
switch (question) {
case Q_REPAIR_MAP:
for (uint32_t i = 0; i < arenap->btt_info.external_nlba; i++) {
uint32_t lba = map_get_postmap_lba(arenap, i);
if (lba >= arenap->btt_info.internal_nlba)
continue;
if (!util_isset(loc->dup_bitmap, lba))
continue;
arenap->map[i] = BTT_MAP_ENTRY_ERROR | lba;
util_clrbit(loc->dup_bitmap, lba);
CHECK_INFO(ppc,
"arena %u: storing 0x%x at %u BTT Map entry",
arenap->id, arenap->map[i], i);
}
while (list_pop(loc->list_inval, &inval)) {
if (!list_pop(loc->list_unmap, &unmap)) {
ppc->result = CHECK_RESULT_ERROR;
return -1;
}
arenap->map[inval] = unmap | BTT_MAP_ENTRY_ERROR;
CHECK_INFO(ppc, "arena %u: storing 0x%x at %u BTT Map "
"entry", arenap->id, arenap->map[inval], inval);
}
break;
case Q_REPAIR_FLOG:
while (list_pop(loc->list_flog_inval, &inval)) {
if (!list_pop(loc->list_unmap, &unmap)) {
ppc->result = CHECK_RESULT_ERROR;
return -1;
}
struct btt_flog *flog = (struct btt_flog *)
(arenap->flog + inval * BTT_FLOG_PAIR_ALIGN);
memset(&flog[1], 0, sizeof(flog[1]));
uint32_t entry = unmap | BTT_MAP_ENTRY_ERROR;
flog[0].lba = inval;
flog[0].new_map = entry;
flog[0].old_map = entry;
flog[0].seq = 1;
CHECK_INFO(ppc, "arena %u: repairing BTT Flog at %u "
"with free block entry 0x%x", loc->arenap->id,
inval, entry);
}
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 = init,
},
{
.check = arena_map_flog_check,
},
{
.fix = arena_map_flog_fix,
},
{
.check = cleanup,
},
{
.check = NULL,
.fix = NULL,
},
};
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, step->fix))
return 0;
cleanup(ppc, loc);
return -1;
}
void
check_btt_map_flog(PMEMpoolcheck *ppc)
{
LOG(3, NULL);
location *loc = check_get_step_data(ppc->data);
if (ppc->pool->blk_no_layout)
return;
if (!loc->arenap && loc->narena == 0 &&
ppc->result != CHECK_RESULT_PROCESS_ANSWERS) {
CHECK_INFO(ppc, "checking BTT Map and Flog");
loc->arenap = TAILQ_FIRST(&ppc->pool->arenas);
loc->narena = 0;
}
while (loc->arenap != NULL) {
if (ppc->result != CHECK_RESULT_PROCESS_ANSWERS &&
loc->step == 0) {
CHECK_INFO(ppc, "arena %u: checking BTT Map and Flog",
loc->narena);
}
while (CHECK_NOT_COMPLETE(loc, steps)) {
if (step_exe(ppc, loc))
return;
}
loc->arenap = TAILQ_NEXT(loc->arenap, next);
loc->narena++;
loc->step = 0;
}
}