}
}
+void dbg_dump_lpt_info(struct ubifs_info *c)
+{
+ int i;
+
+ spin_lock(&dbg_lock);
+ printk(KERN_DEBUG "\tlpt_sz: %lld\n", c->lpt_sz);
+ printk(KERN_DEBUG "\tpnode_sz: %d\n", c->pnode_sz);
+ printk(KERN_DEBUG "\tnnode_sz: %d\n", c->nnode_sz);
+ printk(KERN_DEBUG "\tltab_sz: %d\n", c->ltab_sz);
+ printk(KERN_DEBUG "\tlsave_sz: %d\n", c->lsave_sz);
+ printk(KERN_DEBUG "\tbig_lpt: %d\n", c->big_lpt);
+ printk(KERN_DEBUG "\tlpt_hght: %d\n", c->lpt_hght);
+ printk(KERN_DEBUG "\tpnode_cnt: %d\n", c->pnode_cnt);
+ printk(KERN_DEBUG "\tnnode_cnt: %d\n", c->nnode_cnt);
+ printk(KERN_DEBUG "\tdirty_pn_cnt: %d\n", c->dirty_pn_cnt);
+ printk(KERN_DEBUG "\tdirty_nn_cnt: %d\n", c->dirty_nn_cnt);
+ printk(KERN_DEBUG "\tlsave_cnt: %d\n", c->lsave_cnt);
+ printk(KERN_DEBUG "\tspace_bits: %d\n", c->space_bits);
+ printk(KERN_DEBUG "\tlpt_lnum_bits: %d\n", c->lpt_lnum_bits);
+ printk(KERN_DEBUG "\tlpt_offs_bits: %d\n", c->lpt_offs_bits);
+ printk(KERN_DEBUG "\tlpt_spc_bits: %d\n", c->lpt_spc_bits);
+ printk(KERN_DEBUG "\tpcnt_bits: %d\n", c->pcnt_bits);
+ printk(KERN_DEBUG "\tlnum_bits: %d\n", c->lnum_bits);
+ printk(KERN_DEBUG "\tLPT root is at %d:%d\n", c->lpt_lnum, c->lpt_offs);
+ printk(KERN_DEBUG "\tLPT head is at %d:%d\n",
+ c->nhead_lnum, c->nhead_offs);
+ printk(KERN_DEBUG "\tLPT ltab is at %d:%d\n", c->ltab_lnum, c->ltab_offs);
+ if (c->big_lpt)
+ printk(KERN_DEBUG "\tLPT lsave is at %d:%d\n",
+ c->lsave_lnum, c->lsave_offs);
+ for (i = 0; i < c->lpt_lebs; i++)
+ printk(KERN_DEBUG "\tLPT LEB %d free %d dirty %d tgc %d "
+ "cmt %d\n", i + c->lpt_first, c->ltab[i].free,
+ c->ltab[i].dirty, c->ltab[i].tgc, c->ltab[i].cmt);
+ spin_unlock(&dbg_lock);
+}
+
void dbg_dump_leb(const struct ubifs_info *c, int lnum)
{
struct ubifs_scan_leb *sleb;
void dbg_dump_budg(struct ubifs_info *c);
void dbg_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp);
void dbg_dump_lprops(struct ubifs_info *c);
+void dbg_dump_lpt_info(struct ubifs_info *c);
void dbg_dump_leb(const struct ubifs_info *c, int lnum);
void dbg_dump_znode(const struct ubifs_info *c,
const struct ubifs_znode *znode);
int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot);
int dbg_check_cats(struct ubifs_info *c);
int dbg_check_ltab(struct ubifs_info *c);
+int dbg_chk_lpt_free_spc(struct ubifs_info *c);
+int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len);
int dbg_check_synced_i_size(struct inode *inode);
int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir);
int dbg_check_tnc(struct ubifs_info *c, int extra);
#define dbg_dump_budg(c) ({})
#define dbg_dump_lprop(c, lp) ({})
#define dbg_dump_lprops(c) ({})
+#define dbg_dump_lpt_info(c) ({})
#define dbg_dump_leb(c, lnum) ({})
#define dbg_dump_znode(c, znode) ({})
#define dbg_dump_heap(c, heap, cat) ({})
#define dbg_check_old_index(c, zroot) 0
#define dbg_check_cats(c) 0
#define dbg_check_ltab(c) 0
+#define dbg_chk_lpt_free_spc(c) 0
+#define dbg_chk_lpt_sz(c, action, len) 0
#define dbg_check_synced_i_size(inode) 0
#define dbg_check_dir_size(c, dir) 0
#define dbg_check_tnc(c, x) 0
return 0;
}
}
- dbg_err("last LEB %d", *lnum);
- dump_stack();
return -ENOSPC;
}
int lnum, offs, len, alen, done_lsave, done_ltab, err;
struct ubifs_cnode *cnode;
+ err = dbg_chk_lpt_sz(c, 0, 0);
+ if (err)
+ return err;
cnode = c->lpt_cnext;
if (!cnode)
return 0;
c->lsave_lnum = lnum;
c->lsave_offs = offs;
offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
}
if (offs + c->ltab_sz <= c->leb_size) {
c->ltab_lnum = lnum;
c->ltab_offs = offs;
offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
}
do {
while (offs + len > c->leb_size) {
alen = ALIGN(offs, c->min_io_size);
upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, alen - offs);
err = alloc_lpt_leb(c, &lnum);
if (err)
- return err;
+ goto no_space;
offs = 0;
ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last);
c->lsave_lnum = lnum;
c->lsave_offs = offs;
offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
continue;
}
if (!done_ltab) {
c->ltab_lnum = lnum;
c->ltab_offs = offs;
offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
continue;
}
break;
c->lpt_offs = offs;
}
offs += len;
+ dbg_chk_lpt_sz(c, 1, len);
cnode = cnode->cnext;
} while (cnode && cnode != c->lpt_cnext);
if (offs + c->lsave_sz > c->leb_size) {
alen = ALIGN(offs, c->min_io_size);
upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, alen - offs);
err = alloc_lpt_leb(c, &lnum);
if (err)
- return err;
+ goto no_space;
offs = 0;
ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last);
c->lsave_lnum = lnum;
c->lsave_offs = offs;
offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
}
/* Make sure to place LPT's own lprops table */
if (offs + c->ltab_sz > c->leb_size) {
alen = ALIGN(offs, c->min_io_size);
upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 2, alen - offs);
err = alloc_lpt_leb(c, &lnum);
if (err)
- return err;
+ goto no_space;
offs = 0;
ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last);
c->ltab_lnum = lnum;
c->ltab_offs = offs;
offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
}
alen = ALIGN(offs, c->min_io_size);
upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
+ dbg_chk_lpt_sz(c, 4, alen - offs);
+ err = dbg_chk_lpt_sz(c, 3, alen);
+ if (err)
+ return err;
return 0;
+
+no_space:
+ ubifs_err("LPT out of space");
+ dbg_err("LPT out of space at LEB %d:%d needing %d, done_ltab %d, "
+ "done_lsave %d", lnum, offs, len, done_ltab, done_lsave);
+ dbg_dump_lpt_info(c);
+ return err;
}
/**
*lnum = i + c->lpt_first;
return 0;
}
- dbg_err("last LEB %d", *lnum);
- dump_stack();
return -ENOSPC;
}
done_lsave = 1;
ubifs_pack_lsave(c, buf + offs, c->lsave);
offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
}
if (offs + c->ltab_sz <= c->leb_size) {
done_ltab = 1;
ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
}
/* Loop for each cnode */
alen, UBI_SHORTTERM);
if (err)
return err;
+ dbg_chk_lpt_sz(c, 4, alen - wlen);
}
+ dbg_chk_lpt_sz(c, 2, 0);
err = realloc_lpt_leb(c, &lnum);
if (err)
- return err;
+ goto no_space;
offs = 0;
from = 0;
ubifs_assert(lnum >= c->lpt_first &&
done_lsave = 1;
ubifs_pack_lsave(c, buf + offs, c->lsave);
offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
continue;
}
if (!done_ltab) {
done_ltab = 1;
ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
continue;
}
break;
clear_bit(COW_ZNODE, &cnode->flags);
smp_mb__after_clear_bit();
offs += len;
+ dbg_chk_lpt_sz(c, 1, len);
cnode = cnode->cnext;
} while (cnode && cnode != c->lpt_cnext);
UBI_SHORTTERM);
if (err)
return err;
+ dbg_chk_lpt_sz(c, 2, alen - wlen);
err = realloc_lpt_leb(c, &lnum);
if (err)
- return err;
+ goto no_space;
offs = 0;
ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last);
done_lsave = 1;
ubifs_pack_lsave(c, buf + offs, c->lsave);
offs += c->lsave_sz;
+ dbg_chk_lpt_sz(c, 1, c->lsave_sz);
}
/* Make sure to place LPT's own lprops table */
UBI_SHORTTERM);
if (err)
return err;
+ dbg_chk_lpt_sz(c, 2, alen - wlen);
err = realloc_lpt_leb(c, &lnum);
if (err)
- return err;
+ goto no_space;
offs = 0;
ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last);
done_ltab = 1;
ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
offs += c->ltab_sz;
+ dbg_chk_lpt_sz(c, 1, c->ltab_sz);
}
/* Write remaining data in buffer */
err = ubifs_leb_write(c, lnum, buf + from, from, alen, UBI_SHORTTERM);
if (err)
return err;
+
+ dbg_chk_lpt_sz(c, 4, alen - wlen);
+ err = dbg_chk_lpt_sz(c, 3, ALIGN(offs, c->min_io_size));
+ if (err)
+ return err;
+
c->nhead_lnum = lnum;
c->nhead_offs = ALIGN(offs, c->min_io_size);
dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
if (c->big_lpt)
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
+
return 0;
+
+no_space:
+ ubifs_err("LPT out of space mismatch");
+ dbg_err("LPT out of space mismatch at LEB %d:%d needing %d, done_ltab "
+ "%d, done_lsave %d", lnum, offs, len, done_ltab, done_lsave);
+ dbg_dump_lpt_info(c);
+ return err;
}
/**
dbg_lp("");
mutex_lock(&c->lp_mutex);
+ err = dbg_chk_lpt_free_spc(c);
+ if (err)
+ goto out;
err = dbg_check_ltab(c);
if (err)
goto out;
return 0;
}
+/**
+ * dbg_chk_lpt_free_spc - check LPT free space is enough to write entire LPT.
+ * @c: the UBIFS file-system description object
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_chk_lpt_free_spc(struct ubifs_info *c)
+{
+ long long free = 0;
+ int i;
+
+ for (i = 0; i < c->lpt_lebs; i++) {
+ if (c->ltab[i].tgc || c->ltab[i].cmt)
+ continue;
+ if (i + c->lpt_first == c->nhead_lnum)
+ free += c->leb_size - c->nhead_offs;
+ else if (c->ltab[i].free == c->leb_size)
+ free += c->leb_size;
+ }
+ if (free < c->lpt_sz) {
+ dbg_err("LPT space error: free %lld lpt_sz %lld",
+ free, c->lpt_sz);
+ dbg_dump_lpt_info(c);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * dbg_chk_lpt_sz - check LPT does not write more than LPT size.
+ * @c: the UBIFS file-system description object
+ * @action: action
+ * @len: length written
+ *
+ * This function returns %0 on success and a negative error code on failure.
+ */
+int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len)
+{
+ long long chk_lpt_sz, lpt_sz;
+ int err = 0;
+
+ switch (action) {
+ case 0:
+ c->chk_lpt_sz = 0;
+ c->chk_lpt_sz2 = 0;
+ c->chk_lpt_lebs = 0;
+ c->chk_lpt_wastage = 0;
+ if (c->dirty_pn_cnt > c->pnode_cnt) {
+ dbg_err("dirty pnodes %d exceed max %d",
+ c->dirty_pn_cnt, c->pnode_cnt);
+ err = -EINVAL;
+ }
+ if (c->dirty_nn_cnt > c->nnode_cnt) {
+ dbg_err("dirty nnodes %d exceed max %d",
+ c->dirty_nn_cnt, c->nnode_cnt);
+ err = -EINVAL;
+ }
+ return err;
+ case 1:
+ c->chk_lpt_sz += len;
+ return 0;
+ case 2:
+ c->chk_lpt_sz += len;
+ c->chk_lpt_wastage += len;
+ c->chk_lpt_lebs += 1;
+ return 0;
+ case 3:
+ chk_lpt_sz = c->leb_size;
+ chk_lpt_sz *= c->chk_lpt_lebs;
+ chk_lpt_sz += len - c->nhead_offs;
+ if (c->chk_lpt_sz != chk_lpt_sz) {
+ dbg_err("LPT wrote %lld but space used was %lld",
+ c->chk_lpt_sz, chk_lpt_sz);
+ err = -EINVAL;
+ }
+ if (c->chk_lpt_sz > c->lpt_sz) {
+ dbg_err("LPT wrote %lld but lpt_sz is %lld",
+ c->chk_lpt_sz, c->lpt_sz);
+ err = -EINVAL;
+ }
+ if (c->chk_lpt_sz2 && c->chk_lpt_sz != c->chk_lpt_sz2) {
+ dbg_err("LPT layout size %lld but wrote %lld",
+ c->chk_lpt_sz, c->chk_lpt_sz2);
+ err = -EINVAL;
+ }
+ if (c->chk_lpt_sz2 && c->new_nhead_offs != len) {
+ dbg_err("LPT new nhead offs: expected %d was %d",
+ c->new_nhead_offs, len);
+ err = -EINVAL;
+ }
+ lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
+ lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
+ lpt_sz += c->ltab_sz;
+ if (c->big_lpt)
+ lpt_sz += c->lsave_sz;
+ if (c->chk_lpt_sz - c->chk_lpt_wastage > lpt_sz) {
+ dbg_err("LPT chk_lpt_sz %lld + waste %lld exceeds %lld",
+ c->chk_lpt_sz, c->chk_lpt_wastage, lpt_sz);
+ err = -EINVAL;
+ }
+ if (err)
+ dbg_dump_lpt_info(c);
+ c->chk_lpt_sz2 = c->chk_lpt_sz;
+ c->chk_lpt_sz = 0;
+ c->chk_lpt_wastage = 0;
+ c->chk_lpt_lebs = 0;
+ c->new_nhead_offs = len;
+ return err;
+ case 4:
+ c->chk_lpt_sz += len;
+ c->chk_lpt_wastage += len;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
#endif /* CONFIG_UBIFS_FS_DEBUG */