diff options
Diffstat (limited to 'ubifs-utils/libubifs/lprops.c')
-rw-r--r-- | ubifs-utils/libubifs/lprops.c | 484 |
1 files changed, 10 insertions, 474 deletions
diff --git a/ubifs-utils/libubifs/lprops.c b/ubifs-utils/libubifs/lprops.c index 6d6cd85..84cdb35 100644 --- a/ubifs-utils/libubifs/lprops.c +++ b/ubifs-utils/libubifs/lprops.c @@ -16,7 +16,13 @@ * an empty LEB for the journal, or a very dirty LEB for garbage collection. */ +#include "linux_err.h" +#include "bitops.h" +#include "kmem.h" #include "ubifs.h" +#include "defs.h" +#include "debug.h" +#include "misc.h" /** * get_heap_comp_val - get the LEB properties value for heap comparisons. @@ -47,7 +53,8 @@ static int get_heap_comp_val(struct ubifs_lprops *lprops, int cat) * is either the amount of free space or the amount of dirty space, depending * on the category. */ -static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, +static void move_up_lpt_heap(__unused struct ubifs_info *c, + struct ubifs_lpt_heap *heap, struct ubifs_lprops *lprops, int cat) { int val1, val2, hpos; @@ -84,7 +91,8 @@ static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, * greater. In the case of LPT's category heaps, the value is either the amount * of free space or the amount of dirty space, depending on the category. */ -static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, +static void adjust_lpt_heap(__unused struct ubifs_info *c, + struct ubifs_lpt_heap *heap, struct ubifs_lprops *lprops, int hpos, int cat) { int val1, val2, val3, cpos; @@ -191,16 +199,13 @@ static int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops, lprops->hpos = cpos; heap->arr[cpos] = lprops; move_up_lpt_heap(c, heap, lprops, cat); - dbg_check_heap(c, heap, cat, lprops->hpos); return 1; /* Added to heap */ } - dbg_check_heap(c, heap, cat, -1); return 0; /* Not added to heap */ } else { lprops->hpos = heap->cnt++; heap->arr[lprops->hpos] = lprops; move_up_lpt_heap(c, heap, lprops, cat); - dbg_check_heap(c, heap, cat, lprops->hpos); return 1; /* Added to heap */ } } @@ -226,7 +231,6 @@ static void remove_from_lpt_heap(struct ubifs_info *c, heap->arr[hpos]->hpos = hpos; adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat); } - dbg_check_heap(c, heap, cat, -1); } /** @@ -837,471 +841,3 @@ const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c) ubifs_assert(c, lprops->free + lprops->dirty == c->leb_size); return lprops; } - -/* - * Everything below is related to debugging. - */ - -/** - * dbg_check_cats - check category heaps and lists. - * @c: UBIFS file-system description object - * - * This function returns %0 on success and a negative error code on failure. - */ -int dbg_check_cats(struct ubifs_info *c) -{ - struct ubifs_lprops *lprops; - struct list_head *pos; - int i, cat; - - if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c)) - return 0; - - list_for_each_entry(lprops, &c->empty_list, list) { - if (lprops->free != c->leb_size) { - ubifs_err(c, "non-empty LEB %d on empty list (free %d dirty %d flags %d)", - lprops->lnum, lprops->free, lprops->dirty, - lprops->flags); - return -EINVAL; - } - if (lprops->flags & LPROPS_TAKEN) { - ubifs_err(c, "taken LEB %d on empty list (free %d dirty %d flags %d)", - lprops->lnum, lprops->free, lprops->dirty, - lprops->flags); - return -EINVAL; - } - } - - i = 0; - list_for_each_entry(lprops, &c->freeable_list, list) { - if (lprops->free + lprops->dirty != c->leb_size) { - ubifs_err(c, "non-freeable LEB %d on freeable list (free %d dirty %d flags %d)", - lprops->lnum, lprops->free, lprops->dirty, - lprops->flags); - return -EINVAL; - } - if (lprops->flags & LPROPS_TAKEN) { - ubifs_err(c, "taken LEB %d on freeable list (free %d dirty %d flags %d)", - lprops->lnum, lprops->free, lprops->dirty, - lprops->flags); - return -EINVAL; - } - i += 1; - } - if (i != c->freeable_cnt) { - ubifs_err(c, "freeable list count %d expected %d", i, - c->freeable_cnt); - return -EINVAL; - } - - i = 0; - list_for_each(pos, &c->idx_gc) - i += 1; - if (i != c->idx_gc_cnt) { - ubifs_err(c, "idx_gc list count %d expected %d", i, - c->idx_gc_cnt); - return -EINVAL; - } - - list_for_each_entry(lprops, &c->frdi_idx_list, list) { - if (lprops->free + lprops->dirty != c->leb_size) { - ubifs_err(c, "non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)", - lprops->lnum, lprops->free, lprops->dirty, - lprops->flags); - return -EINVAL; - } - if (lprops->flags & LPROPS_TAKEN) { - ubifs_err(c, "taken LEB %d on frdi_idx list (free %d dirty %d flags %d)", - lprops->lnum, lprops->free, lprops->dirty, - lprops->flags); - return -EINVAL; - } - if (!(lprops->flags & LPROPS_INDEX)) { - ubifs_err(c, "non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)", - lprops->lnum, lprops->free, lprops->dirty, - lprops->flags); - return -EINVAL; - } - } - - for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) { - struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1]; - - for (i = 0; i < heap->cnt; i++) { - lprops = heap->arr[i]; - if (!lprops) { - ubifs_err(c, "null ptr in LPT heap cat %d", cat); - return -EINVAL; - } - if (lprops->hpos != i) { - ubifs_err(c, "bad ptr in LPT heap cat %d", cat); - return -EINVAL; - } - if (lprops->flags & LPROPS_TAKEN) { - ubifs_err(c, "taken LEB in LPT heap cat %d", cat); - return -EINVAL; - } - } - } - - return 0; -} - -void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat, - int add_pos) -{ - int i = 0, j, err = 0; - - if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c)) - return; - - for (i = 0; i < heap->cnt; i++) { - struct ubifs_lprops *lprops = heap->arr[i]; - struct ubifs_lprops *lp; - - if (i != add_pos) - if ((lprops->flags & LPROPS_CAT_MASK) != cat) { - err = 1; - goto out; - } - if (lprops->hpos != i) { - err = 2; - goto out; - } - lp = ubifs_lpt_lookup(c, lprops->lnum); - if (IS_ERR(lp)) { - err = 3; - goto out; - } - if (lprops != lp) { - ubifs_err(c, "lprops %zx lp %zx lprops->lnum %d lp->lnum %d", - (size_t)lprops, (size_t)lp, lprops->lnum, - lp->lnum); - err = 4; - goto out; - } - for (j = 0; j < i; j++) { - lp = heap->arr[j]; - if (lp == lprops) { - err = 5; - goto out; - } - if (lp->lnum == lprops->lnum) { - err = 6; - goto out; - } - } - } -out: - if (err) { - ubifs_err(c, "failed cat %d hpos %d err %d", cat, i, err); - dump_stack(); - ubifs_dump_heap(c, heap, cat); - } -} - -/** - * scan_check_cb - scan callback. - * @c: the UBIFS file-system description object - * @lp: LEB properties to scan - * @in_tree: whether the LEB properties are in main memory - * @lst: lprops statistics to update - * - * This function returns a code that indicates whether the scan should continue - * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree - * in main memory (%LPT_SCAN_ADD), or whether the scan should stop - * (%LPT_SCAN_STOP). - */ -static int scan_check_cb(struct ubifs_info *c, - const struct ubifs_lprops *lp, int in_tree, - struct ubifs_lp_stats *lst) -{ - struct ubifs_scan_leb *sleb; - struct ubifs_scan_node *snod; - int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty, ret; - void *buf = NULL; - - cat = lp->flags & LPROPS_CAT_MASK; - if (cat != LPROPS_UNCAT) { - cat = ubifs_categorize_lprops(c, lp); - if (cat != (lp->flags & LPROPS_CAT_MASK)) { - ubifs_err(c, "bad LEB category %d expected %d", - (lp->flags & LPROPS_CAT_MASK), cat); - return -EINVAL; - } - } - - /* Check lp is on its category list (if it has one) */ - if (in_tree) { - struct list_head *list = NULL; - - switch (cat) { - case LPROPS_EMPTY: - list = &c->empty_list; - break; - case LPROPS_FREEABLE: - list = &c->freeable_list; - break; - case LPROPS_FRDI_IDX: - list = &c->frdi_idx_list; - break; - case LPROPS_UNCAT: - list = &c->uncat_list; - break; - } - if (list) { - struct ubifs_lprops *lprops; - int found = 0; - - list_for_each_entry(lprops, list, list) { - if (lprops == lp) { - found = 1; - break; - } - } - if (!found) { - ubifs_err(c, "bad LPT list (category %d)", cat); - return -EINVAL; - } - } - } - - /* Check lp is on its category heap (if it has one) */ - if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) { - struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1]; - - if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) || - lp != heap->arr[lp->hpos]) { - ubifs_err(c, "bad LPT heap (category %d)", cat); - return -EINVAL; - } - } - - /* - * After an unclean unmount, empty and freeable LEBs - * may contain garbage - do not scan them. - */ - if (lp->free == c->leb_size) { - lst->empty_lebs += 1; - lst->total_free += c->leb_size; - lst->total_dark += ubifs_calc_dark(c, c->leb_size); - return LPT_SCAN_CONTINUE; - } - if (lp->free + lp->dirty == c->leb_size && - !(lp->flags & LPROPS_INDEX)) { - lst->total_free += lp->free; - lst->total_dirty += lp->dirty; - lst->total_dark += ubifs_calc_dark(c, c->leb_size); - return LPT_SCAN_CONTINUE; - } - - buf = __vmalloc(c->leb_size, GFP_NOFS); - if (!buf) - return -ENOMEM; - - sleb = ubifs_scan(c, lnum, 0, buf, 0); - if (IS_ERR(sleb)) { - ret = PTR_ERR(sleb); - if (ret == -EUCLEAN) { - ubifs_dump_lprops(c); - ubifs_dump_budg(c, &c->bi); - } - goto out; - } - - is_idx = -1; - list_for_each_entry(snod, &sleb->nodes, list) { - int found, level = 0; - - cond_resched(); - - if (is_idx == -1) - is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0; - - if (is_idx && snod->type != UBIFS_IDX_NODE) { - ubifs_err(c, "indexing node in data LEB %d:%d", - lnum, snod->offs); - goto out_destroy; - } - - if (snod->type == UBIFS_IDX_NODE) { - struct ubifs_idx_node *idx = snod->node; - - key_read(c, ubifs_idx_key(c, idx), &snod->key); - level = le16_to_cpu(idx->level); - } - - found = ubifs_tnc_has_node(c, &snod->key, level, lnum, - snod->offs, is_idx); - if (found) { - if (found < 0) - goto out_destroy; - used += ALIGN(snod->len, 8); - } - } - - free = c->leb_size - sleb->endpt; - dirty = sleb->endpt - used; - - if (free > c->leb_size || free < 0 || dirty > c->leb_size || - dirty < 0) { - ubifs_err(c, "bad calculated accounting for LEB %d: free %d, dirty %d", - lnum, free, dirty); - goto out_destroy; - } - - if (lp->free + lp->dirty == c->leb_size && - free + dirty == c->leb_size) - if ((is_idx && !(lp->flags & LPROPS_INDEX)) || - (!is_idx && free == c->leb_size) || - lp->free == c->leb_size) { - /* - * Empty or freeable LEBs could contain index - * nodes from an uncompleted commit due to an - * unclean unmount. Or they could be empty for - * the same reason. Or it may simply not have been - * unmapped. - */ - free = lp->free; - dirty = lp->dirty; - is_idx = 0; - } - - if (is_idx && lp->free + lp->dirty == free + dirty && - lnum != c->ihead_lnum) { - /* - * After an unclean unmount, an index LEB could have a different - * amount of free space than the value recorded by lprops. That - * is because the in-the-gaps method may use free space or - * create free space (as a side-effect of using ubi_leb_change - * and not writing the whole LEB). The incorrect free space - * value is not a problem because the index is only ever - * allocated empty LEBs, so there will never be an attempt to - * write to the free space at the end of an index LEB - except - * by the in-the-gaps method for which it is not a problem. - */ - free = lp->free; - dirty = lp->dirty; - } - - if (lp->free != free || lp->dirty != dirty) - goto out_print; - - if (is_idx && !(lp->flags & LPROPS_INDEX)) { - if (free == c->leb_size) - /* Free but not unmapped LEB, it's fine */ - is_idx = 0; - else { - ubifs_err(c, "indexing node without indexing flag"); - goto out_print; - } - } - - if (!is_idx && (lp->flags & LPROPS_INDEX)) { - ubifs_err(c, "data node with indexing flag"); - goto out_print; - } - - if (free == c->leb_size) - lst->empty_lebs += 1; - - if (is_idx) - lst->idx_lebs += 1; - - if (!(lp->flags & LPROPS_INDEX)) - lst->total_used += c->leb_size - free - dirty; - lst->total_free += free; - lst->total_dirty += dirty; - - if (!(lp->flags & LPROPS_INDEX)) { - int spc = free + dirty; - - if (spc < c->dead_wm) - lst->total_dead += spc; - else - lst->total_dark += ubifs_calc_dark(c, spc); - } - - ubifs_scan_destroy(sleb); - vfree(buf); - return LPT_SCAN_CONTINUE; - -out_print: - ubifs_err(c, "bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d", - lnum, lp->free, lp->dirty, lp->flags, free, dirty); - ubifs_dump_leb(c, lnum); -out_destroy: - ubifs_scan_destroy(sleb); - ret = -EINVAL; -out: - vfree(buf); - return ret; -} - -/** - * dbg_check_lprops - check all LEB properties. - * @c: UBIFS file-system description object - * - * This function checks all LEB properties and makes sure they are all correct. - * It returns zero if everything is fine, %-EINVAL if there is an inconsistency - * and other negative error codes in case of other errors. This function is - * called while the file system is locked (because of commit start), so no - * additional locking is required. Note that locking the LPT mutex would cause - * a circular lock dependency with the TNC mutex. - */ -int dbg_check_lprops(struct ubifs_info *c) -{ - int i, err; - struct ubifs_lp_stats lst; - - if (!dbg_is_chk_lprops(c)) - return 0; - - /* - * As we are going to scan the media, the write buffers have to be - * synchronized. - */ - for (i = 0; i < c->jhead_cnt; i++) { - err = ubifs_wbuf_sync(&c->jheads[i].wbuf); - if (err) - return err; - } - - memset(&lst, 0, sizeof(struct ubifs_lp_stats)); - err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1, - (ubifs_lpt_scan_callback)scan_check_cb, - &lst); - if (err && err != -ENOSPC) - goto out; - - if (lst.empty_lebs != c->lst.empty_lebs || - lst.idx_lebs != c->lst.idx_lebs || - lst.total_free != c->lst.total_free || - lst.total_dirty != c->lst.total_dirty || - lst.total_used != c->lst.total_used) { - ubifs_err(c, "bad overall accounting"); - ubifs_err(c, "calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld", - lst.empty_lebs, lst.idx_lebs, lst.total_free, - lst.total_dirty, lst.total_used); - ubifs_err(c, "read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld", - c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free, - c->lst.total_dirty, c->lst.total_used); - err = -EINVAL; - goto out; - } - - if (lst.total_dead != c->lst.total_dead || - lst.total_dark != c->lst.total_dark) { - ubifs_err(c, "bad dead/dark space accounting"); - ubifs_err(c, "calculated: total_dead %lld, total_dark %lld", - lst.total_dead, lst.total_dark); - ubifs_err(c, "read from lprops: total_dead %lld, total_dark %lld", - c->lst.total_dead, c->lst.total_dark); - err = -EINVAL; - goto out; - } - - err = dbg_check_cats(c); -out: - return err; -} |