Commit 396b122f authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'linux-next' of git://git.infradead.org/ubifs-2.6

* 'linux-next' of git://git.infradead.org/ubifs-2.6: (25 commits)
  UBIFS: fix ubifs_compress commentary
  UBIFS: amend printk
  UBIFS: do not read unnecessary bytes when unpacking bits
  UBIFS: check buffer length when scanning for LPT nodes
  UBIFS: correct condition to eliminate unecessary assignment
  UBIFS: add more debugging messages for LPT
  UBIFS: fix bulk-read handling uptodate pages
  UBIFS: improve garbage collection
  UBIFS: allow for sync_fs when read-only
  UBIFS: commit on sync_fs
  UBIFS: correct comment for commit_on_unmount
  UBIFS: update dbg_dump_inode
  UBIFS: fix commentary
  UBIFS: fix races in bit-fields
  UBIFS: ensure data read beyond i_size is zeroed out correctly
  UBIFS: correct key comparison
  UBIFS: use bit-fields when possible
  UBIFS: check data CRC when in error state
  UBIFS: improve znode splitting rules
  UBIFS: add no_chk_data_crc mount option
  ...
parents ed402af3 54779aab
...@@ -86,6 +86,15 @@ norm_unmount (*) commit on unmount; the journal is committed ...@@ -86,6 +86,15 @@ norm_unmount (*) commit on unmount; the journal is committed
fast_unmount do not commit on unmount; this option makes fast_unmount do not commit on unmount; this option makes
unmount faster, but the next mount slower unmount faster, but the next mount slower
because of the need to replay the journal. because of the need to replay the journal.
bulk_read read more in one go to take advantage of flash
media that read faster sequentially
no_bulk_read (*) do not bulk-read
no_chk_data_crc skip checking of CRCs on data nodes in order to
improve read performance. Use this option only
if the flash media is highly reliable. The effect
of this option is that corruption of the contents
of a file can go unnoticed.
chk_data_crc (*) do not skip checking CRCs on data nodes
Quick usage instructions Quick usage instructions
......
...@@ -414,19 +414,21 @@ static int do_budget_space(struct ubifs_info *c) ...@@ -414,19 +414,21 @@ static int do_budget_space(struct ubifs_info *c)
* @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt - * @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt -
* @c->lst.taken_empty_lebs * @c->lst.taken_empty_lebs
* *
* @empty_lebs are available because they are empty. @freeable_cnt are * @c->lst.empty_lebs are available because they are empty.
* available because they contain only free and dirty space and the * @c->freeable_cnt are available because they contain only free and
* index allocation always occurs after wbufs are synch'ed. * dirty space, @c->idx_gc_cnt are available because they are index
* @idx_gc_cnt are available because they are index LEBs that have been * LEBs that have been garbage collected and are awaiting the commit
* garbage collected (including trivial GC) and are awaiting the commit * before they can be used. And the in-the-gaps method will grab these
* before they can be unmapped - note that the in-the-gaps method will * if it needs them. @c->lst.taken_empty_lebs are empty LEBs that have
* grab these if it needs them. @taken_empty_lebs are empty_lebs that * already been allocated for some purpose.
* have already been allocated for some purpose (also includes those
* LEBs on the @idx_gc list).
* *
* Note, @taken_empty_lebs may temporarily be higher by one because of * Note, @c->idx_gc_cnt is included to both @c->lst.empty_lebs (because
* the way we serialize LEB allocations and budgeting. See a comment in * these LEBs are empty) and to @c->lst.taken_empty_lebs (because they
* 'ubifs_find_free_space()'. * are taken until after the commit).
*
* Note, @c->lst.taken_empty_lebs may temporarily be higher by one
* because of the way we serialize LEB allocations and budgeting. See a
* comment in 'ubifs_find_free_space()'.
*/ */
lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt - lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt -
c->lst.taken_empty_lebs; c->lst.taken_empty_lebs;
......
...@@ -91,8 +91,6 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT]; ...@@ -91,8 +91,6 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
* *
* Note, if the input buffer was not compressed, it is copied to the output * Note, if the input buffer was not compressed, it is copied to the output
* buffer and %UBIFS_COMPR_NONE is returned in @compr_type. * buffer and %UBIFS_COMPR_NONE is returned in @compr_type.
*
* This functions returns %0 on success or a negative error code on failure.
*/ */
void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len, void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len,
int *compr_type) int *compr_type)
......
...@@ -222,30 +222,38 @@ void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode) ...@@ -222,30 +222,38 @@ void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode)
{ {
const struct ubifs_inode *ui = ubifs_inode(inode); const struct ubifs_inode *ui = ubifs_inode(inode);
printk(KERN_DEBUG "inode %lu\n", inode->i_ino); printk(KERN_DEBUG "Dump in-memory inode:");
printk(KERN_DEBUG "size %llu\n", printk(KERN_DEBUG "\tinode %lu\n", inode->i_ino);
printk(KERN_DEBUG "\tsize %llu\n",
(unsigned long long)i_size_read(inode)); (unsigned long long)i_size_read(inode));
printk(KERN_DEBUG "nlink %u\n", inode->i_nlink); printk(KERN_DEBUG "\tnlink %u\n", inode->i_nlink);
printk(KERN_DEBUG "uid %u\n", (unsigned int)inode->i_uid); printk(KERN_DEBUG "\tuid %u\n", (unsigned int)inode->i_uid);
printk(KERN_DEBUG "gid %u\n", (unsigned int)inode->i_gid); printk(KERN_DEBUG "\tgid %u\n", (unsigned int)inode->i_gid);
printk(KERN_DEBUG "atime %u.%u\n", printk(KERN_DEBUG "\tatime %u.%u\n",
(unsigned int)inode->i_atime.tv_sec, (unsigned int)inode->i_atime.tv_sec,
(unsigned int)inode->i_atime.tv_nsec); (unsigned int)inode->i_atime.tv_nsec);
printk(KERN_DEBUG "mtime %u.%u\n", printk(KERN_DEBUG "\tmtime %u.%u\n",
(unsigned int)inode->i_mtime.tv_sec, (unsigned int)inode->i_mtime.tv_sec,
(unsigned int)inode->i_mtime.tv_nsec); (unsigned int)inode->i_mtime.tv_nsec);
printk(KERN_DEBUG "ctime %u.%u\n", printk(KERN_DEBUG "\tctime %u.%u\n",
(unsigned int)inode->i_ctime.tv_sec, (unsigned int)inode->i_ctime.tv_sec,
(unsigned int)inode->i_ctime.tv_nsec); (unsigned int)inode->i_ctime.tv_nsec);
printk(KERN_DEBUG "creat_sqnum %llu\n", ui->creat_sqnum); printk(KERN_DEBUG "\tcreat_sqnum %llu\n", ui->creat_sqnum);
printk(KERN_DEBUG "xattr_size %u\n", ui->xattr_size); printk(KERN_DEBUG "\txattr_size %u\n", ui->xattr_size);
printk(KERN_DEBUG "xattr_cnt %u\n", ui->xattr_cnt); printk(KERN_DEBUG "\txattr_cnt %u\n", ui->xattr_cnt);
printk(KERN_DEBUG "xattr_names %u\n", ui->xattr_names); printk(KERN_DEBUG "\txattr_names %u\n", ui->xattr_names);
printk(KERN_DEBUG "dirty %u\n", ui->dirty); printk(KERN_DEBUG "\tdirty %u\n", ui->dirty);
printk(KERN_DEBUG "xattr %u\n", ui->xattr); printk(KERN_DEBUG "\txattr %u\n", ui->xattr);
printk(KERN_DEBUG "flags %d\n", ui->flags); printk(KERN_DEBUG "\tbulk_read %u\n", ui->xattr);
printk(KERN_DEBUG "compr_type %d\n", ui->compr_type); printk(KERN_DEBUG "\tsynced_i_size %llu\n",
printk(KERN_DEBUG "data_len %d\n", ui->data_len); (unsigned long long)ui->synced_i_size);
printk(KERN_DEBUG "\tui_size %llu\n",
(unsigned long long)ui->ui_size);
printk(KERN_DEBUG "\tflags %d\n", ui->flags);
printk(KERN_DEBUG "\tcompr_type %d\n", ui->compr_type);
printk(KERN_DEBUG "\tlast_page_read %lu\n", ui->last_page_read);
printk(KERN_DEBUG "\tread_in_a_row %lu\n", ui->read_in_a_row);
printk(KERN_DEBUG "\tdata_len %d\n", ui->data_len);
} }
void dbg_dump_node(const struct ubifs_info *c, const void *node) void dbg_dump_node(const struct ubifs_info *c, const void *node)
...@@ -647,6 +655,43 @@ void dbg_dump_lprops(struct ubifs_info *c) ...@@ -647,6 +655,43 @@ void dbg_dump_lprops(struct ubifs_info *c)
} }
} }
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) void dbg_dump_leb(const struct ubifs_info *c, int lnum)
{ {
struct ubifs_scan_leb *sleb; struct ubifs_scan_leb *sleb;
......
...@@ -224,6 +224,7 @@ void dbg_dump_lstats(const struct ubifs_lp_stats *lst); ...@@ -224,6 +224,7 @@ void dbg_dump_lstats(const struct ubifs_lp_stats *lst);
void dbg_dump_budg(struct ubifs_info *c); 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_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp);
void dbg_dump_lprops(struct ubifs_info *c); 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_leb(const struct ubifs_info *c, int lnum);
void dbg_dump_znode(const struct ubifs_info *c, void dbg_dump_znode(const struct ubifs_info *c,
const struct ubifs_znode *znode); const struct ubifs_znode *znode);
...@@ -249,6 +250,8 @@ int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot); ...@@ -249,6 +250,8 @@ int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot);
int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot); int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot);
int dbg_check_cats(struct ubifs_info *c); int dbg_check_cats(struct ubifs_info *c);
int dbg_check_ltab(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_synced_i_size(struct inode *inode);
int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir); int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir);
int dbg_check_tnc(struct ubifs_info *c, int extra); int dbg_check_tnc(struct ubifs_info *c, int extra);
...@@ -367,6 +370,7 @@ static inline int dbg_change(struct ubi_volume_desc *desc, int lnum, ...@@ -367,6 +370,7 @@ static inline int dbg_change(struct ubi_volume_desc *desc, int lnum,
#define dbg_dump_budg(c) ({}) #define dbg_dump_budg(c) ({})
#define dbg_dump_lprop(c, lp) ({}) #define dbg_dump_lprop(c, lp) ({})
#define dbg_dump_lprops(c) ({}) #define dbg_dump_lprops(c) ({})
#define dbg_dump_lpt_info(c) ({})
#define dbg_dump_leb(c, lnum) ({}) #define dbg_dump_leb(c, lnum) ({})
#define dbg_dump_znode(c, znode) ({}) #define dbg_dump_znode(c, znode) ({})
#define dbg_dump_heap(c, heap, cat) ({}) #define dbg_dump_heap(c, heap, cat) ({})
...@@ -379,6 +383,8 @@ static inline int dbg_change(struct ubi_volume_desc *desc, int lnum, ...@@ -379,6 +383,8 @@ static inline int dbg_change(struct ubi_volume_desc *desc, int lnum,
#define dbg_check_old_index(c, zroot) 0 #define dbg_check_old_index(c, zroot) 0
#define dbg_check_cats(c) 0 #define dbg_check_cats(c) 0
#define dbg_check_ltab(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_synced_i_size(inode) 0
#define dbg_check_dir_size(c, dir) 0 #define dbg_check_dir_size(c, dir) 0
#define dbg_check_tnc(c, x) 0 #define dbg_check_tnc(c, x) 0
......
...@@ -147,6 +147,12 @@ static int do_readpage(struct page *page) ...@@ -147,6 +147,12 @@ static int do_readpage(struct page *page)
err = ret; err = ret;
if (err != -ENOENT) if (err != -ENOENT)
break; break;
} else if (block + 1 == beyond) {
int dlen = le32_to_cpu(dn->size);
int ilen = i_size & (UBIFS_BLOCK_SIZE - 1);
if (ilen && ilen < dlen)
memset(addr + ilen, 0, dlen - ilen);
} }
} }
if (++i >= UBIFS_BLOCKS_PER_PAGE) if (++i >= UBIFS_BLOCKS_PER_PAGE)
...@@ -577,8 +583,262 @@ static int ubifs_write_end(struct file *file, struct address_space *mapping, ...@@ -577,8 +583,262 @@ static int ubifs_write_end(struct file *file, struct address_space *mapping,
return copied; return copied;
} }
/**
* populate_page - copy data nodes into a page for bulk-read.
* @c: UBIFS file-system description object
* @page: page
* @bu: bulk-read information
* @n: next zbranch slot
*
* This function returns %0 on success and a negative error code on failure.
*/
static int populate_page(struct ubifs_info *c, struct page *page,
struct bu_info *bu, int *n)
{
int i = 0, nn = *n, offs = bu->zbranch[0].offs, hole = 0, read = 0;
struct inode *inode = page->mapping->host;
loff_t i_size = i_size_read(inode);
unsigned int page_block;
void *addr, *zaddr;
pgoff_t end_index;
dbg_gen("ino %lu, pg %lu, i_size %lld, flags %#lx",
inode->i_ino, page->index, i_size, page->flags);
addr = zaddr = kmap(page);
end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
if (!i_size || page->index > end_index) {
hole = 1;
memset(addr, 0, PAGE_CACHE_SIZE);
goto out_hole;
}
page_block = page->index << UBIFS_BLOCKS_PER_PAGE_SHIFT;
while (1) {
int err, len, out_len, dlen;
if (nn >= bu->cnt) {
hole = 1;
memset(addr, 0, UBIFS_BLOCK_SIZE);
} else if (key_block(c, &bu->zbranch[nn].key) == page_block) {
struct ubifs_data_node *dn;
dn = bu->buf + (bu->zbranch[nn].offs - offs);
ubifs_assert(dn->ch.sqnum >
ubifs_inode(inode)->creat_sqnum);
len = le32_to_cpu(dn->size);
if (len <= 0 || len > UBIFS_BLOCK_SIZE)
goto out_err;
dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
out_len = UBIFS_BLOCK_SIZE;
err = ubifs_decompress(&dn->data, dlen, addr, &out_len,
le16_to_cpu(dn->compr_type));
if (err || len != out_len)
goto out_err;
if (len < UBIFS_BLOCK_SIZE)
memset(addr + len, 0, UBIFS_BLOCK_SIZE - len);
nn += 1;
read = (i << UBIFS_BLOCK_SHIFT) + len;
} else if (key_block(c, &bu->zbranch[nn].key) < page_block) {
nn += 1;
continue;
} else {
hole = 1;
memset(addr, 0, UBIFS_BLOCK_SIZE);
}
if (++i >= UBIFS_BLOCKS_PER_PAGE)
break;
addr += UBIFS_BLOCK_SIZE;
page_block += 1;
}
if (end_index == page->index) {
int len = i_size & (PAGE_CACHE_SIZE - 1);
if (len && len < read)
memset(zaddr + len, 0, read - len);
}
out_hole:
if (hole) {
SetPageChecked(page);
dbg_gen("hole");
}
SetPageUptodate(page);
ClearPageError(page);
flush_dcache_page(page);
kunmap(page);
*n = nn;
return 0;
out_err:
ClearPageUptodate(page);
SetPageError(page);
flush_dcache_page(page);
kunmap(page);
ubifs_err("bad data node (block %u, inode %lu)",
page_block, inode->i_ino);
return -EINVAL;
}
/**
* ubifs_do_bulk_read - do bulk-read.
* @c: UBIFS file-system description object
* @page1: first page
*
* This function returns %1 if the bulk-read is done, otherwise %0 is returned.
*/
static int ubifs_do_bulk_read(struct ubifs_info *c, struct page *page1)
{
pgoff_t offset = page1->index, end_index;
struct address_space *mapping = page1->mapping;
struct inode *inode = mapping->host;
struct ubifs_inode *ui = ubifs_inode(inode);
struct bu_info *bu;
int err, page_idx, page_cnt, ret = 0, n = 0;
loff_t isize;
bu = kmalloc(sizeof(struct bu_info), GFP_NOFS);
if (!bu)
return 0;
bu->buf_len = c->bulk_read_buf_size;
bu->buf = kmalloc(bu->buf_len, GFP_NOFS);
if (!bu->buf)
goto out_free;
data_key_init(c, &bu->key, inode->i_ino,
offset << UBIFS_BLOCKS_PER_PAGE_SHIFT);
err = ubifs_tnc_get_bu_keys(c, bu);
if (err)
goto out_warn;
if (bu->eof) {
/* Turn off bulk-read at the end of the file */
ui->read_in_a_row = 1;
ui->bulk_read = 0;
}
page_cnt = bu->blk_cnt >> UBIFS_BLOCKS_PER_PAGE_SHIFT;
if (!page_cnt) {
/*
* This happens when there are multiple blocks per page and the
* blocks for the first page we are looking for, are not
* together. If all the pages were like this, bulk-read would
* reduce performance, so we turn it off for a while.
*/
ui->read_in_a_row = 0;
ui->bulk_read = 0;
goto out_free;
}
if (bu->cnt) {
err = ubifs_tnc_bulk_read(c, bu);
if (err)
goto out_warn;
}
err = populate_page(c, page1, bu, &n);
if (err)
goto out_warn;
unlock_page(page1);
ret = 1;
isize = i_size_read(inode);
if (isize == 0)
goto out_free;
end_index = ((isize - 1) >> PAGE_CACHE_SHIFT);
for (page_idx = 1; page_idx < page_cnt; page_idx++) {
pgoff_t page_offset = offset + page_idx;
struct page *page;
if (page_offset > end_index)
break;
page = find_or_create_page(mapping, page_offset,
GFP_NOFS | __GFP_COLD);
if (!page)
break;
if (!PageUptodate(page))
err = populate_page(c, page, bu, &n);
unlock_page(page);
page_cache_release(page);
if (err)
break;
}
ui->last_page_read = offset + page_idx - 1;
out_free:
kfree(bu->buf);
kfree(bu);
return ret;
out_warn:
ubifs_warn("ignoring error %d and skipping bulk-read", err);
goto out_free;
}
/**
* ubifs_bulk_read - determine whether to bulk-read and, if so, do it.
* @page: page from which to start bulk-read.
*
* Some flash media are capable of reading sequentially at faster rates. UBIFS
* bulk-read facility is designed to take advantage of that, by reading in one
* go consecutive data nodes that are also located consecutively in the same
* LEB. This function returns %1 if a bulk-read is done and %0 otherwise.
*/
static int ubifs_bulk_read(struct page *page)
{
struct inode *inode = page->mapping->host;
struct ubifs_info *c = inode->i_sb->s_fs_info;
struct ubifs_inode *ui = ubifs_inode(inode);
pgoff_t index = page->index, last_page_read = ui->last_page_read;
int ret = 0;
ui->last_page_read = index;
if (!c->bulk_read)
return 0;
/*
* Bulk-read is protected by ui_mutex, but it is an optimization, so
* don't bother if we cannot lock the mutex.
*/
if (!mutex_trylock(&ui->ui_mutex))
return 0;
if (index != last_page_read + 1) {
/* Turn off bulk-read if we stop reading sequentially */
ui->read_in_a_row = 1;
if (ui->bulk_read)
ui->bulk_read = 0;
goto out_unlock;
}
if (!ui->bulk_read) {
ui->read_in_a_row += 1;
if (ui->read_in_a_row < 3)
goto out_unlock;
/* Three reads in a row, so switch on bulk-read */
ui->bulk_read = 1;
}
ret = ubifs_do_bulk_read(c, page);
out_unlock:
mutex_unlock(&ui->ui_mutex);
return ret;
}
static int ubifs_readpage(struct file *file, struct page *page) static int ubifs_readpage(struct file *file, struct page *page)
{ {
if (ubifs_bulk_read(page))
return 0;
do_readpage(page); do_readpage(page);
unlock_page(page); unlock_page(page);
return 0; return 0;
......
...@@ -901,11 +901,11 @@ static int get_idx_gc_leb(struct ubifs_info *c) ...@@ -901,11 +901,11 @@ static int get_idx_gc_leb(struct ubifs_info *c)
* it is needed now for this commit. * it is needed now for this commit.
*/ */
lp = ubifs_lpt_lookup_dirty(c, lnum); lp = ubifs_lpt_lookup_dirty(c, lnum);
if (unlikely(IS_ERR(lp))) if (IS_ERR(lp))
return PTR_ERR(lp); return PTR_ERR(lp);
lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC, lp = ubifs_change_lp(c, lp, LPROPS_NC, LPROPS_NC,
lp->flags | LPROPS_INDEX, -1); lp->flags | LPROPS_INDEX, -1);
if (unlikely(IS_ERR(lp))) if (IS_ERR(lp))
return PTR_ERR(lp); return PTR_ERR(lp);
dbg_find("LEB %d, dirty %d and free %d flags %#x", dbg_find("LEB %d, dirty %d and free %d flags %#x",
lp->lnum, lp->dirty, lp->free, lp->flags); lp->lnum, lp->dirty, lp->free, lp->flags);
......
...@@ -95,6 +95,48 @@ static int switch_gc_head(struct ubifs_info *c) ...@@ -95,6 +95,48 @@ static int switch_gc_head(struct ubifs_info *c)
return err; return err;
} }
/**
* joinup - bring data nodes for an inode together.
* @c: UBIFS file-system description object
* @sleb: describes scanned LEB
* @inum: inode number
* @blk: block number
* @data: list to which to add data nodes
*
* This function looks at the first few nodes in the scanned LEB @sleb and adds
* them to @data if they are data nodes from @inum and have a larger block
* number than @blk. This function returns %0 on success and a negative error
* code on failure.
*/
static int joinup(struct ubifs_info *c, struct ubifs_scan_leb *sleb, ino_t inum,
unsigned int blk, struct list_head *data)
{
int err, cnt = 6, lnum = sleb->lnum, offs;
struct ubifs_scan_node *snod, *tmp;
union ubifs_key *key;
list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
key = &snod->key;
if (key_inum(c, key) == inum &&
key_type(c, key) == UBIFS_DATA_KEY &&
key_block(c, key) > blk) {
offs = snod->offs;
err = ubifs_tnc_has_node(c, key, 0, lnum, offs, 0);
if (err < 0)
return err;
list_del(&snod->list);
if (err) {
list_add_tail(&snod->list, data);
blk = key_block(c, key);
} else
kfree(snod);
cnt = 6;
} else if (--cnt == 0)
break;
}
return 0;
}
/** /**
* move_nodes - move nodes. * move_nodes - move nodes.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
...@@ -116,16 +158,21 @@ static int switch_gc_head(struct ubifs_info *c) ...@@ -116,16 +158,21 @@ static int switch_gc_head(struct ubifs_info *c)
static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
{ {
struct ubifs_scan_node *snod, *tmp; struct ubifs_scan_node *snod, *tmp;
struct list_head large, medium, small; struct list_head data, large, medium, small;
struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf;
int avail, err, min = INT_MAX; int avail, err, min = INT_MAX;
unsigned int blk = 0;
ino_t inum = 0;
INIT_LIST_HEAD(&data);
INIT_LIST_HEAD(&large); INIT_LIST_HEAD(&large);
INIT_LIST_HEAD(&medium); INIT_LIST_HEAD(&medium);
INIT_LIST_HEAD(&small); INIT_LIST_HEAD(&small);
list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) { while (!list_empty(&sleb->nodes)) {
struct list_head *lst; struct list_head *lst = sleb->nodes.next;
snod = list_entry(lst, struct ubifs_scan_node, list);
ubifs_assert(snod->type != UBIFS_IDX_NODE); ubifs_assert(snod->type != UBIFS_IDX_NODE);
ubifs_assert(snod->type != UBIFS_REF_NODE); ubifs_assert(snod->type != UBIFS_REF_NODE);
...@@ -136,7 +183,6 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) ...@@ -136,7 +183,6 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
if (err < 0) if (err < 0)
goto out; goto out;
lst = &snod->list;
list_del(lst); list_del(lst);
if (!err) { if (!err) {
/* The node is obsolete, remove it from the list */ /* The node is obsolete, remove it from the list */
...@@ -145,15 +191,30 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) ...@@ -145,15 +191,30 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
} }
/* /*
* Sort the list of nodes so that large nodes go first, and * Sort the list of nodes so that data nodes go first, large
* small nodes go last. * nodes go second, and small nodes go last.
*/ */
if (snod->len > MEDIUM_NODE_WM) if (key_type(c, &snod->key) == UBIFS_DATA_KEY) {
list_add(lst, &large); if (inum != key_inum(c, &snod->key)) {
if (inum) {
/*
* Try to move data nodes from the same
* inode together.
*/
err = joinup(c, sleb, inum, blk, &data);
if (err)
goto out;
}
inum = key_inum(c, &snod->key);
blk = key_block(c, &snod->key);
}
list_add_tail(lst, &data);
} else if (snod->len > MEDIUM_NODE_WM)
list_add_tail(lst, &large);
else if (snod->len > SMALL_NODE_WM) else if (snod->len > SMALL_NODE_WM)
list_add(lst, &medium); list_add_tail(lst, &medium);
else else
list_add(lst, &small); list_add_tail(lst, &small);
/* And find the smallest node */ /* And find the smallest node */
if (snod->len < min) if (snod->len < min)
...@@ -164,6 +225,7 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) ...@@ -164,6 +225,7 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
* Join the tree lists so that we'd have one roughly sorted list * Join the tree lists so that we'd have one roughly sorted list
* ('large' will be the head of the joined list). * ('large' will be the head of the joined list).
*/ */
list_splice(&data, &large);
list_splice(&medium, large.prev); list_splice(&medium, large.prev);
list_splice(&small, large.prev); list_splice(&small, large.prev);
...@@ -653,7 +715,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c) ...@@ -653,7 +715,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c)
*/ */
while (1) { while (1) {
lp = ubifs_fast_find_freeable(c); lp = ubifs_fast_find_freeable(c);
if (unlikely(IS_ERR(lp))) { if (IS_ERR(lp)) {
err = PTR_ERR(lp); err = PTR_ERR(lp);
goto out; goto out;
} }
...@@ -665,7 +727,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c) ...@@ -665,7 +727,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c)
if (err) if (err)
goto out; goto out;
lp = ubifs_change_lp(c, lp, c->leb_size, 0, lp->flags, 0); lp = ubifs_change_lp(c, lp, c->leb_size, 0, lp->flags, 0);
if (unlikely(IS_ERR(lp))) { if (IS_ERR(lp)) {
err = PTR_ERR(lp); err = PTR_ERR(lp);
goto out; goto out;
} }
...@@ -680,7 +742,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c) ...@@ -680,7 +742,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c)
/* Record index freeable LEBs for unmapping after commit */ /* Record index freeable LEBs for unmapping after commit */
while (1) { while (1) {
lp = ubifs_fast_find_frdi_idx(c); lp = ubifs_fast_find_frdi_idx(c);
if (unlikely(IS_ERR(lp))) { if (IS_ERR(lp)) {
err = PTR_ERR(lp); err = PTR_ERR(lp);
goto out; goto out;
} }
...@@ -696,7 +758,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c) ...@@ -696,7 +758,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c)
/* Don't release the LEB until after the next commit */ /* Don't release the LEB until after the next commit */
flags = (lp->flags | LPROPS_TAKEN) ^ LPROPS_INDEX; flags = (lp->flags | LPROPS_TAKEN) ^ LPROPS_INDEX;
lp = ubifs_change_lp(c, lp, c->leb_size, 0, flags, 1); lp = ubifs_change_lp(c, lp, c->leb_size, 0, flags, 1);
if (unlikely(IS_ERR(lp))) { if (IS_ERR(lp)) {
err = PTR_ERR(lp); err = PTR_ERR(lp);
kfree(idx_gc); kfree(idx_gc);
goto out; goto out;
......
...@@ -62,6 +62,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err) ...@@ -62,6 +62,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err)
{ {
if (!c->ro_media) { if (!c->ro_media) {
c->ro_media = 1; c->ro_media = 1;
c->no_chk_data_crc = 0;
ubifs_warn("switched to read-only mode, error %d", err); ubifs_warn("switched to read-only mode, error %d", err);
dbg_dump_stack(); dbg_dump_stack();
} }
...@@ -74,6 +75,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err) ...@@ -74,6 +75,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err)
* @lnum: logical eraseblock number * @lnum: logical eraseblock number
* @offs: offset within the logical eraseblock * @offs: offset within the logical eraseblock
* @quiet: print no messages * @quiet: print no messages
* @chk_crc: indicates whether to always check the CRC
* *
* This function checks node magic number and CRC checksum. This function also * This function checks node magic number and CRC checksum. This function also
* validates node length to prevent UBIFS from becoming crazy when an attacker * validates node length to prevent UBIFS from becoming crazy when an attacker
...@@ -85,7 +87,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err) ...@@ -85,7 +87,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err)
* or magic. * or magic.
*/ */
int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
int offs, int quiet) int offs, int quiet, int chk_crc)
{ {
int err = -EINVAL, type, node_len; int err = -EINVAL, type, node_len;
uint32_t crc, node_crc, magic; uint32_t crc, node_crc, magic;
...@@ -121,6 +123,10 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, ...@@ -121,6 +123,10 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
node_len > c->ranges[type].max_len) node_len > c->ranges[type].max_len)
goto out_len; goto out_len;
if (!chk_crc && type == UBIFS_DATA_NODE && !c->always_chk_crc)
if (c->no_chk_data_crc)
return 0;
crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8);
node_crc = le32_to_cpu(ch->crc); node_crc = le32_to_cpu(ch->crc);
if (crc != node_crc) { if (crc != node_crc) {
...@@ -722,7 +728,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, ...@@ -722,7 +728,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
goto out; goto out;
} }
err = ubifs_check_node(c, buf, lnum, offs, 0); err = ubifs_check_node(c, buf, lnum, offs, 0, 0);
if (err) { if (err) {
ubifs_err("expected node type %d", type); ubifs_err("expected node type %d", type);
return err; return err;
...@@ -781,7 +787,7 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len, ...@@ -781,7 +787,7 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len,
goto out; goto out;
} }
err = ubifs_check_node(c, buf, lnum, offs, 0); err = ubifs_check_node(c, buf, lnum, offs, 0, 0);
if (err) { if (err) {
ubifs_err("expected node type %d", type); ubifs_err("expected node type %d", type);
return err; return err;
......
...@@ -484,7 +484,7 @@ static inline void key_copy(const struct ubifs_info *c, ...@@ -484,7 +484,7 @@ static inline void key_copy(const struct ubifs_info *c,
* @key2: the second key to compare * @key2: the second key to compare
* *
* This function compares 2 keys and returns %-1 if @key1 is less than * This function compares 2 keys and returns %-1 if @key1 is less than
* @key2, 0 if the keys are equivalent and %1 if @key1 is greater than @key2. * @key2, %0 if the keys are equivalent and %1 if @key1 is greater than @key2.
*/ */
static inline int keys_cmp(const struct ubifs_info *c, static inline int keys_cmp(const struct ubifs_info *c,
const union ubifs_key *key1, const union ubifs_key *key1,
...@@ -502,6 +502,26 @@ static inline int keys_cmp(const struct ubifs_info *c, ...@@ -502,6 +502,26 @@ static inline int keys_cmp(const struct ubifs_info *c,
return 0; return 0;
} }
/**
* keys_eq - determine if keys are equivalent.
* @c: UBIFS file-system description object
* @key1: the first key to compare
* @key2: the second key to compare
*
* This function compares 2 keys and returns %1 if @key1 is equal to @key2 and
* %0 if not.
*/
static inline int keys_eq(const struct ubifs_info *c,
const union ubifs_key *key1,
const union ubifs_key *key2)
{
if (key1->u32[0] != key2->u32[0])
return 0;
if (key1->u32[1] != key2->u32[1])
return 0;
return 1;
}
/** /**
* is_hash_key - is a key vulnerable to hash collisions. * is_hash_key - is a key vulnerable to hash collisions.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
......
...@@ -125,6 +125,7 @@ static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, ...@@ -125,6 +125,7 @@ static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
} }
} }
} }
/* Not greater than parent, so compare to children */ /* Not greater than parent, so compare to children */
while (1) { while (1) {
/* Compare to left child */ /* Compare to left child */
...@@ -459,18 +460,6 @@ static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops) ...@@ -459,18 +460,6 @@ static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
} }
} }
/**
* ubifs_get_lprops - get reference to LEB properties.
* @c: the UBIFS file-system description object
*
* This function locks lprops. Lprops have to be unlocked by
* 'ubifs_release_lprops()'.
*/
void ubifs_get_lprops(struct ubifs_info *c)
{
mutex_lock(&c->lp_mutex);
}
/** /**
* calc_dark - calculate LEB dark space size. * calc_dark - calculate LEB dark space size.
* @c: the UBIFS file-system description object * @c: the UBIFS file-system description object
...@@ -576,7 +565,6 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, ...@@ -576,7 +565,6 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
ubifs_assert(!(lprops->free & 7) && !(lprops->dirty & 7)); ubifs_assert(!(lprops->free & 7) && !(lprops->dirty & 7));
spin_lock(&c->space_lock); spin_lock(&c->space_lock);
if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size) if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
c->lst.taken_empty_lebs -= 1; c->lst.taken_empty_lebs -= 1;
...@@ -637,30 +625,11 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, ...@@ -637,30 +625,11 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
c->lst.taken_empty_lebs += 1; c->lst.taken_empty_lebs += 1;
change_category(c, lprops); change_category(c, lprops);
c->idx_gc_cnt += idx_gc_cnt; c->idx_gc_cnt += idx_gc_cnt;
spin_unlock(&c->space_lock); spin_unlock(&c->space_lock);
return lprops; return lprops;
} }
/**
* ubifs_release_lprops - release lprops lock.
* @c: the UBIFS file-system description object
*
* This function has to be called after each 'ubifs_get_lprops()' call to
* unlock lprops.
*/
void ubifs_release_lprops(struct ubifs_info *c)
{
ubifs_assert(mutex_is_locked(&c->lp_mutex));
ubifs_assert(c->lst.empty_lebs >= 0 &&
c->lst.empty_lebs <= c->main_lebs);
mutex_unlock(&c->lp_mutex);
}
/** /**
* ubifs_get_lp_stats - get lprops statistics. * ubifs_get_lp_stats - get lprops statistics.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
...@@ -1262,7 +1231,6 @@ static int scan_check_cb(struct ubifs_info *c, ...@@ -1262,7 +1231,6 @@ static int scan_check_cb(struct ubifs_info *c,
} }
ubifs_scan_destroy(sleb); ubifs_scan_destroy(sleb);
return LPT_SCAN_CONTINUE; return LPT_SCAN_CONTINUE;
out_print: out_print:
......
...@@ -109,7 +109,8 @@ static void do_calc_lpt_geom(struct ubifs_info *c) ...@@ -109,7 +109,8 @@ static void do_calc_lpt_geom(struct ubifs_info *c)
c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
c->lpt_sz += c->ltab_sz; c->lpt_sz += c->ltab_sz;
c->lpt_sz += c->lsave_sz; if (c->big_lpt)
c->lpt_sz += c->lsave_sz;
/* Add wastage */ /* Add wastage */
sz = c->lpt_sz; sz = c->lpt_sz;
...@@ -287,25 +288,56 @@ uint32_t ubifs_unpack_bits(uint8_t **addr, int *pos, int nrbits) ...@@ -287,25 +288,56 @@ uint32_t ubifs_unpack_bits(uint8_t **addr, int *pos, int nrbits)
const int k = 32 - nrbits; const int k = 32 - nrbits;
uint8_t *p = *addr; uint8_t *p = *addr;
int b = *pos; int b = *pos;
uint32_t val; uint32_t uninitialized_var(val);
const int bytes = (nrbits + b + 7) >> 3;
ubifs_assert(nrbits > 0); ubifs_assert(nrbits > 0);
ubifs_assert(nrbits <= 32); ubifs_assert(nrbits <= 32);
ubifs_assert(*pos >= 0); ubifs_assert(*pos >= 0);
ubifs_assert(*pos < 8); ubifs_assert(*pos < 8);
if (b) { if (b) {
val = p[1] | ((uint32_t)p[2] << 8) | ((uint32_t)p[3] << 16) | switch (bytes) {
((uint32_t)p[4] << 24); case 2:
val = p[1];
break;
case 3:
val = p[1] | ((uint32_t)p[2] << 8);
break;
case 4:
val = p[1] | ((uint32_t)p[2] << 8) |
((uint32_t)p[3] << 16);
break;
case 5:
val = p[1] | ((uint32_t)p[2] << 8) |
((uint32_t)p[3] << 16) |
((uint32_t)p[4] << 24);
}
val <<= (8 - b); val <<= (8 - b);
val |= *p >> b; val |= *p >> b;
nrbits += b; nrbits += b;
} else } else {
val = p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | switch (bytes) {
((uint32_t)p[3] << 24); case 1:
val = p[0];
break;
case 2:
val = p[0] | ((uint32_t)p[1] << 8);
break;
case 3:
val = p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16);
break;
case 4:
val = p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24);
break;
}
}
val <<= k; val <<= k;
val >>= k; val >>= k;
b = nrbits & 7; b = nrbits & 7;
p += nrbits / 8; p += nrbits >> 3;
*addr = p; *addr = p;
*pos = b; *pos = b;
ubifs_assert((val >> nrbits) == 0 || nrbits - b == 32); ubifs_assert((val >> nrbits) == 0 || nrbits - b == 32);
......
...@@ -177,8 +177,6 @@ static int alloc_lpt_leb(struct ubifs_info *c, int *lnum) ...@@ -177,8 +177,6 @@ static int alloc_lpt_leb(struct ubifs_info *c, int *lnum)
return 0; return 0;
} }
} }
dbg_err("last LEB %d", *lnum);
dump_stack();
return -ENOSPC; return -ENOSPC;
} }
...@@ -193,6 +191,9 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -193,6 +191,9 @@ static int layout_cnodes(struct ubifs_info *c)
int lnum, offs, len, alen, done_lsave, done_ltab, err; int lnum, offs, len, alen, done_lsave, done_ltab, err;
struct ubifs_cnode *cnode; struct ubifs_cnode *cnode;
err = dbg_chk_lpt_sz(c, 0, 0);
if (err)
return err;
cnode = c->lpt_cnext; cnode = c->lpt_cnext;
if (!cnode) if (!cnode)
return 0; return 0;
...@@ -206,6 +207,7 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -206,6 +207,7 @@ static int layout_cnodes(struct ubifs_info *c)
c->lsave_lnum = lnum; c->lsave_lnum = lnum;
c->lsave_offs = offs; c->lsave_offs = offs;
offs += c->lsave_sz; offs += c->lsave_sz;
dbg_chk_lpt_sz(c, 1, c->lsave_sz);
} }
if (offs + c->ltab_sz <= c->leb_size) { if (offs + c->ltab_sz <= c->leb_size) {
...@@ -213,6 +215,7 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -213,6 +215,7 @@ static int layout_cnodes(struct ubifs_info *c)
c->ltab_lnum = lnum; c->ltab_lnum = lnum;
c->ltab_offs = offs; c->ltab_offs = offs;
offs += c->ltab_sz; offs += c->ltab_sz;
dbg_chk_lpt_sz(c, 1, c->ltab_sz);
} }
do { do {
...@@ -226,9 +229,10 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -226,9 +229,10 @@ static int layout_cnodes(struct ubifs_info *c)
while (offs + len > c->leb_size) { while (offs + len > c->leb_size) {
alen = ALIGN(offs, c->min_io_size); alen = ALIGN(offs, c->min_io_size);
upd_ltab(c, lnum, c->leb_size - alen, alen - offs); upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
dbg_chk_lpt_sz(c, 2, alen - offs);
err = alloc_lpt_leb(c, &lnum); err = alloc_lpt_leb(c, &lnum);
if (err) if (err)
return err; goto no_space;
offs = 0; offs = 0;
ubifs_assert(lnum >= c->lpt_first && ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last); lnum <= c->lpt_last);
...@@ -238,6 +242,7 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -238,6 +242,7 @@ static int layout_cnodes(struct ubifs_info *c)
c->lsave_lnum = lnum; c->lsave_lnum = lnum;
c->lsave_offs = offs; c->lsave_offs = offs;
offs += c->lsave_sz; offs += c->lsave_sz;
dbg_chk_lpt_sz(c, 1, c->lsave_sz);
continue; continue;
} }
if (!done_ltab) { if (!done_ltab) {
...@@ -245,6 +250,7 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -245,6 +250,7 @@ static int layout_cnodes(struct ubifs_info *c)
c->ltab_lnum = lnum; c->ltab_lnum = lnum;
c->ltab_offs = offs; c->ltab_offs = offs;
offs += c->ltab_sz; offs += c->ltab_sz;
dbg_chk_lpt_sz(c, 1, c->ltab_sz);
continue; continue;
} }
break; break;
...@@ -257,6 +263,7 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -257,6 +263,7 @@ static int layout_cnodes(struct ubifs_info *c)
c->lpt_offs = offs; c->lpt_offs = offs;
} }
offs += len; offs += len;
dbg_chk_lpt_sz(c, 1, len);
cnode = cnode->cnext; cnode = cnode->cnext;
} while (cnode && cnode != c->lpt_cnext); } while (cnode && cnode != c->lpt_cnext);
...@@ -265,9 +272,10 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -265,9 +272,10 @@ static int layout_cnodes(struct ubifs_info *c)
if (offs + c->lsave_sz > c->leb_size) { if (offs + c->lsave_sz > c->leb_size) {
alen = ALIGN(offs, c->min_io_size); alen = ALIGN(offs, c->min_io_size);
upd_ltab(c, lnum, c->leb_size - alen, alen - offs); upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
dbg_chk_lpt_sz(c, 2, alen - offs);
err = alloc_lpt_leb(c, &lnum); err = alloc_lpt_leb(c, &lnum);
if (err) if (err)
return err; goto no_space;
offs = 0; offs = 0;
ubifs_assert(lnum >= c->lpt_first && ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last); lnum <= c->lpt_last);
...@@ -276,6 +284,7 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -276,6 +284,7 @@ static int layout_cnodes(struct ubifs_info *c)
c->lsave_lnum = lnum; c->lsave_lnum = lnum;
c->lsave_offs = offs; c->lsave_offs = offs;
offs += c->lsave_sz; offs += c->lsave_sz;
dbg_chk_lpt_sz(c, 1, c->lsave_sz);
} }
/* Make sure to place LPT's own lprops table */ /* Make sure to place LPT's own lprops table */
...@@ -283,9 +292,10 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -283,9 +292,10 @@ static int layout_cnodes(struct ubifs_info *c)
if (offs + c->ltab_sz > c->leb_size) { if (offs + c->ltab_sz > c->leb_size) {
alen = ALIGN(offs, c->min_io_size); alen = ALIGN(offs, c->min_io_size);
upd_ltab(c, lnum, c->leb_size - alen, alen - offs); upd_ltab(c, lnum, c->leb_size - alen, alen - offs);
dbg_chk_lpt_sz(c, 2, alen - offs);
err = alloc_lpt_leb(c, &lnum); err = alloc_lpt_leb(c, &lnum);
if (err) if (err)
return err; goto no_space;
offs = 0; offs = 0;
ubifs_assert(lnum >= c->lpt_first && ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last); lnum <= c->lpt_last);
...@@ -294,11 +304,23 @@ static int layout_cnodes(struct ubifs_info *c) ...@@ -294,11 +304,23 @@ static int layout_cnodes(struct ubifs_info *c)
c->ltab_lnum = lnum; c->ltab_lnum = lnum;
c->ltab_offs = offs; c->ltab_offs = offs;
offs += c->ltab_sz; offs += c->ltab_sz;
dbg_chk_lpt_sz(c, 1, c->ltab_sz);
} }
alen = ALIGN(offs, c->min_io_size); alen = ALIGN(offs, c->min_io_size);
upd_ltab(c, lnum, c->leb_size - alen, alen - offs); 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; 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;
} }
/** /**
...@@ -333,8 +355,6 @@ static int realloc_lpt_leb(struct ubifs_info *c, int *lnum) ...@@ -333,8 +355,6 @@ static int realloc_lpt_leb(struct ubifs_info *c, int *lnum)
*lnum = i + c->lpt_first; *lnum = i + c->lpt_first;
return 0; return 0;
} }
dbg_err("last LEB %d", *lnum);
dump_stack();
return -ENOSPC; return -ENOSPC;
} }
...@@ -369,12 +389,14 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -369,12 +389,14 @@ static int write_cnodes(struct ubifs_info *c)
done_lsave = 1; done_lsave = 1;
ubifs_pack_lsave(c, buf + offs, c->lsave); ubifs_pack_lsave(c, buf + offs, c->lsave);
offs += c->lsave_sz; offs += c->lsave_sz;
dbg_chk_lpt_sz(c, 1, c->lsave_sz);
} }
if (offs + c->ltab_sz <= c->leb_size) { if (offs + c->ltab_sz <= c->leb_size) {
done_ltab = 1; done_ltab = 1;
ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
offs += c->ltab_sz; offs += c->ltab_sz;
dbg_chk_lpt_sz(c, 1, c->ltab_sz);
} }
/* Loop for each cnode */ /* Loop for each cnode */
...@@ -392,10 +414,12 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -392,10 +414,12 @@ static int write_cnodes(struct ubifs_info *c)
alen, UBI_SHORTTERM); alen, UBI_SHORTTERM);
if (err) if (err)
return err; return err;
dbg_chk_lpt_sz(c, 4, alen - wlen);
} }
dbg_chk_lpt_sz(c, 2, 0);
err = realloc_lpt_leb(c, &lnum); err = realloc_lpt_leb(c, &lnum);
if (err) if (err)
return err; goto no_space;
offs = 0; offs = 0;
from = 0; from = 0;
ubifs_assert(lnum >= c->lpt_first && ubifs_assert(lnum >= c->lpt_first &&
...@@ -408,12 +432,14 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -408,12 +432,14 @@ static int write_cnodes(struct ubifs_info *c)
done_lsave = 1; done_lsave = 1;
ubifs_pack_lsave(c, buf + offs, c->lsave); ubifs_pack_lsave(c, buf + offs, c->lsave);
offs += c->lsave_sz; offs += c->lsave_sz;
dbg_chk_lpt_sz(c, 1, c->lsave_sz);
continue; continue;
} }
if (!done_ltab) { if (!done_ltab) {
done_ltab = 1; done_ltab = 1;
ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
offs += c->ltab_sz; offs += c->ltab_sz;
dbg_chk_lpt_sz(c, 1, c->ltab_sz);
continue; continue;
} }
break; break;
...@@ -435,6 +461,7 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -435,6 +461,7 @@ static int write_cnodes(struct ubifs_info *c)
clear_bit(COW_ZNODE, &cnode->flags); clear_bit(COW_ZNODE, &cnode->flags);
smp_mb__after_clear_bit(); smp_mb__after_clear_bit();
offs += len; offs += len;
dbg_chk_lpt_sz(c, 1, len);
cnode = cnode->cnext; cnode = cnode->cnext;
} while (cnode && cnode != c->lpt_cnext); } while (cnode && cnode != c->lpt_cnext);
...@@ -448,9 +475,10 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -448,9 +475,10 @@ static int write_cnodes(struct ubifs_info *c)
UBI_SHORTTERM); UBI_SHORTTERM);
if (err) if (err)
return err; return err;
dbg_chk_lpt_sz(c, 2, alen - wlen);
err = realloc_lpt_leb(c, &lnum); err = realloc_lpt_leb(c, &lnum);
if (err) if (err)
return err; goto no_space;
offs = 0; offs = 0;
ubifs_assert(lnum >= c->lpt_first && ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last); lnum <= c->lpt_last);
...@@ -461,6 +489,7 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -461,6 +489,7 @@ static int write_cnodes(struct ubifs_info *c)
done_lsave = 1; done_lsave = 1;
ubifs_pack_lsave(c, buf + offs, c->lsave); ubifs_pack_lsave(c, buf + offs, c->lsave);
offs += c->lsave_sz; offs += c->lsave_sz;
dbg_chk_lpt_sz(c, 1, c->lsave_sz);
} }
/* Make sure to place LPT's own lprops table */ /* Make sure to place LPT's own lprops table */
...@@ -473,9 +502,10 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -473,9 +502,10 @@ static int write_cnodes(struct ubifs_info *c)
UBI_SHORTTERM); UBI_SHORTTERM);
if (err) if (err)
return err; return err;
dbg_chk_lpt_sz(c, 2, alen - wlen);
err = realloc_lpt_leb(c, &lnum); err = realloc_lpt_leb(c, &lnum);
if (err) if (err)
return err; goto no_space;
offs = 0; offs = 0;
ubifs_assert(lnum >= c->lpt_first && ubifs_assert(lnum >= c->lpt_first &&
lnum <= c->lpt_last); lnum <= c->lpt_last);
...@@ -486,6 +516,7 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -486,6 +516,7 @@ static int write_cnodes(struct ubifs_info *c)
done_ltab = 1; done_ltab = 1;
ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); ubifs_pack_ltab(c, buf + offs, c->ltab_cmt);
offs += c->ltab_sz; offs += c->ltab_sz;
dbg_chk_lpt_sz(c, 1, c->ltab_sz);
} }
/* Write remaining data in buffer */ /* Write remaining data in buffer */
...@@ -495,6 +526,12 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -495,6 +526,12 @@ static int write_cnodes(struct ubifs_info *c)
err = ubifs_leb_write(c, lnum, buf + from, from, alen, UBI_SHORTTERM); err = ubifs_leb_write(c, lnum, buf + from, from, alen, UBI_SHORTTERM);
if (err) if (err)
return 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_lnum = lnum;
c->nhead_offs = ALIGN(offs, c->min_io_size); c->nhead_offs = ALIGN(offs, c->min_io_size);
...@@ -503,7 +540,15 @@ static int write_cnodes(struct ubifs_info *c) ...@@ -503,7 +540,15 @@ static int write_cnodes(struct ubifs_info *c)
dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
if (c->big_lpt) if (c->big_lpt)
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
return 0; 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;
} }
/** /**
...@@ -1044,6 +1089,8 @@ static int is_a_node(struct ubifs_info *c, uint8_t *buf, int len) ...@@ -1044,6 +1089,8 @@ static int is_a_node(struct ubifs_info *c, uint8_t *buf, int len)
int pos = 0, node_type, node_len; int pos = 0, node_type, node_len;
uint16_t crc, calc_crc; uint16_t crc, calc_crc;
if (len < UBIFS_LPT_CRC_BYTES + (UBIFS_LPT_TYPE_BITS + 7) / 8)
return 0;
node_type = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_TYPE_BITS); node_type = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_TYPE_BITS);
if (node_type == UBIFS_LPT_NOT_A_NODE) if (node_type == UBIFS_LPT_NOT_A_NODE)
return 0; return 0;
...@@ -1156,6 +1203,9 @@ int ubifs_lpt_start_commit(struct ubifs_info *c) ...@@ -1156,6 +1203,9 @@ int ubifs_lpt_start_commit(struct ubifs_info *c)
dbg_lp(""); dbg_lp("");
mutex_lock(&c->lp_mutex); mutex_lock(&c->lp_mutex);
err = dbg_chk_lpt_free_spc(c);
if (err)
goto out;
err = dbg_check_ltab(c); err = dbg_check_ltab(c);
if (err) if (err)
goto out; goto out;
...@@ -1645,4 +1695,121 @@ int dbg_check_ltab(struct ubifs_info *c) ...@@ -1645,4 +1695,121 @@ int dbg_check_ltab(struct ubifs_info *c)
return 0; 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 */ #endif /* CONFIG_UBIFS_FS_DEBUG */
...@@ -310,4 +310,31 @@ static inline int ubifs_tnc_lookup(struct ubifs_info *c, ...@@ -310,4 +310,31 @@ static inline int ubifs_tnc_lookup(struct ubifs_info *c,
return ubifs_tnc_locate(c, key, node, NULL, NULL); return ubifs_tnc_locate(c, key, node, NULL, NULL);
} }
/**
* ubifs_get_lprops - get reference to LEB properties.
* @c: the UBIFS file-system description object
*
* This function locks lprops. Lprops have to be unlocked by
* 'ubifs_release_lprops()'.
*/
static inline void ubifs_get_lprops(struct ubifs_info *c)
{
mutex_lock(&c->lp_mutex);
}
/**
* ubifs_release_lprops - release lprops lock.
* @c: the UBIFS file-system description object
*
* This function has to be called after each 'ubifs_get_lprops()' call to
* unlock lprops.
*/
static inline void ubifs_release_lprops(struct ubifs_info *c)
{
ubifs_assert(mutex_is_locked(&c->lp_mutex));
ubifs_assert(c->lst.empty_lebs >= 0 &&
c->lst.empty_lebs <= c->main_lebs);
mutex_unlock(&c->lp_mutex);
}
#endif /* __UBIFS_MISC_H__ */ #endif /* __UBIFS_MISC_H__ */
...@@ -87,7 +87,7 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, ...@@ -87,7 +87,7 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum,
dbg_scan("scanning %s", dbg_ntype(ch->node_type)); dbg_scan("scanning %s", dbg_ntype(ch->node_type));
if (ubifs_check_node(c, buf, lnum, offs, quiet)) if (ubifs_check_node(c, buf, lnum, offs, quiet, 1))
return SCANNED_A_CORRUPT_NODE; return SCANNED_A_CORRUPT_NODE;
if (ch->node_type == UBIFS_PAD_NODE) { if (ch->node_type == UBIFS_PAD_NODE) {
......
...@@ -401,6 +401,16 @@ static int ubifs_show_options(struct seq_file *s, struct vfsmount *mnt) ...@@ -401,6 +401,16 @@ static int ubifs_show_options(struct seq_file *s, struct vfsmount *mnt)
else if (c->mount_opts.unmount_mode == 1) else if (c->mount_opts.unmount_mode == 1)
seq_printf(s, ",norm_unmount"); seq_printf(s, ",norm_unmount");
if (c->mount_opts.bulk_read == 2)
seq_printf(s, ",bulk_read");
else if (c->mount_opts.bulk_read == 1)
seq_printf(s, ",no_bulk_read");
if (c->mount_opts.chk_data_crc == 2)
seq_printf(s, ",chk_data_crc");
else if (c->mount_opts.chk_data_crc == 1)
seq_printf(s, ",no_chk_data_crc");
return 0; return 0;
} }
...@@ -408,13 +418,26 @@ static int ubifs_sync_fs(struct super_block *sb, int wait) ...@@ -408,13 +418,26 @@ static int ubifs_sync_fs(struct super_block *sb, int wait)
{ {
struct ubifs_info *c = sb->s_fs_info; struct ubifs_info *c = sb->s_fs_info;
int i, ret = 0, err; int i, ret = 0, err;
long long bud_bytes;
if (c->jheads) if (c->jheads) {
for (i = 0; i < c->jhead_cnt; i++) { for (i = 0; i < c->jhead_cnt; i++) {
err = ubifs_wbuf_sync(&c->jheads[i].wbuf); err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
if (err && !ret) if (err && !ret)
ret = err; ret = err;
} }
/* Commit the journal unless it has too little data */
spin_lock(&c->buds_lock);
bud_bytes = c->bud_bytes;
spin_unlock(&c->buds_lock);
if (bud_bytes > c->leb_size) {
err = ubifs_run_commit(c);
if (err)
return err;
}
}
/* /*
* We ought to call sync for c->ubi but it does not have one. If it had * We ought to call sync for c->ubi but it does not have one. If it had
* it would in turn call mtd->sync, however mtd operations are * it would in turn call mtd->sync, however mtd operations are
...@@ -538,6 +561,18 @@ static int init_constants_early(struct ubifs_info *c) ...@@ -538,6 +561,18 @@ static int init_constants_early(struct ubifs_info *c)
* calculations when reporting free space. * calculations when reporting free space.
*/ */
c->leb_overhead = c->leb_size % UBIFS_MAX_DATA_NODE_SZ; c->leb_overhead = c->leb_size % UBIFS_MAX_DATA_NODE_SZ;
/* Buffer size for bulk-reads */
c->bulk_read_buf_size = UBIFS_MAX_BULK_READ * UBIFS_MAX_DATA_NODE_SZ;
if (c->bulk_read_buf_size > c->leb_size)
c->bulk_read_buf_size = c->leb_size;
if (c->bulk_read_buf_size > 128 * 1024) {
/* Check if we can kmalloc more than 128KiB */
void *try = kmalloc(c->bulk_read_buf_size, GFP_KERNEL);
kfree(try);
if (!try)
c->bulk_read_buf_size = 128 * 1024;
}
return 0; return 0;
} }
...@@ -840,17 +875,29 @@ static int check_volume_empty(struct ubifs_info *c) ...@@ -840,17 +875,29 @@ static int check_volume_empty(struct ubifs_info *c)
* *
* Opt_fast_unmount: do not run a journal commit before un-mounting * Opt_fast_unmount: do not run a journal commit before un-mounting
* Opt_norm_unmount: run a journal commit before un-mounting * Opt_norm_unmount: run a journal commit before un-mounting
* Opt_bulk_read: enable bulk-reads
* Opt_no_bulk_read: disable bulk-reads
* Opt_chk_data_crc: check CRCs when reading data nodes
* Opt_no_chk_data_crc: do not check CRCs when reading data nodes
* Opt_err: just end of array marker * Opt_err: just end of array marker
*/ */
enum { enum {
Opt_fast_unmount, Opt_fast_unmount,
Opt_norm_unmount, Opt_norm_unmount,
Opt_bulk_read,
Opt_no_bulk_read,
Opt_chk_data_crc,
Opt_no_chk_data_crc,
Opt_err, Opt_err,
}; };
static const match_table_t tokens = { static const match_table_t tokens = {
{Opt_fast_unmount, "fast_unmount"}, {Opt_fast_unmount, "fast_unmount"},
{Opt_norm_unmount, "norm_unmount"}, {Opt_norm_unmount, "norm_unmount"},
{Opt_bulk_read, "bulk_read"},
{Opt_no_bulk_read, "no_bulk_read"},
{Opt_chk_data_crc, "chk_data_crc"},
{Opt_no_chk_data_crc, "no_chk_data_crc"},
{Opt_err, NULL}, {Opt_err, NULL},
}; };
...@@ -888,6 +935,22 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options, ...@@ -888,6 +935,22 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options,
c->mount_opts.unmount_mode = 1; c->mount_opts.unmount_mode = 1;
c->fast_unmount = 0; c->fast_unmount = 0;
break; break;
case Opt_bulk_read:
c->mount_opts.bulk_read = 2;
c->bulk_read = 1;
break;
case Opt_no_bulk_read:
c->mount_opts.bulk_read = 1;
c->bulk_read = 0;
break;
case Opt_chk_data_crc:
c->mount_opts.chk_data_crc = 2;
c->no_chk_data_crc = 0;
break;
case Opt_no_chk_data_crc:
c->mount_opts.chk_data_crc = 1;
c->no_chk_data_crc = 1;
break;
default: default:
ubifs_err("unrecognized mount option \"%s\" " ubifs_err("unrecognized mount option \"%s\" "
"or missing value", p); "or missing value", p);
...@@ -996,6 +1059,8 @@ static int mount_ubifs(struct ubifs_info *c) ...@@ -996,6 +1059,8 @@ static int mount_ubifs(struct ubifs_info *c)
goto out_free; goto out_free;
} }
c->always_chk_crc = 1;
err = ubifs_read_superblock(c); err = ubifs_read_superblock(c);
if (err) if (err)
goto out_free; goto out_free;
...@@ -1032,8 +1097,6 @@ static int mount_ubifs(struct ubifs_info *c) ...@@ -1032,8 +1097,6 @@ static int mount_ubifs(struct ubifs_info *c)
/* Create background thread */ /* Create background thread */
c->bgt = kthread_create(ubifs_bg_thread, c, c->bgt_name); c->bgt = kthread_create(ubifs_bg_thread, c, c->bgt_name);
if (!c->bgt)
c->bgt = ERR_PTR(-EINVAL);
if (IS_ERR(c->bgt)) { if (IS_ERR(c->bgt)) {
err = PTR_ERR(c->bgt); err = PTR_ERR(c->bgt);
c->bgt = NULL; c->bgt = NULL;
...@@ -1139,24 +1202,28 @@ static int mount_ubifs(struct ubifs_info *c) ...@@ -1139,24 +1202,28 @@ static int mount_ubifs(struct ubifs_info *c)
if (err) if (err)
goto out_infos; goto out_infos;
c->always_chk_crc = 0;
ubifs_msg("mounted UBI device %d, volume %d, name \"%s\"", ubifs_msg("mounted UBI device %d, volume %d, name \"%s\"",
c->vi.ubi_num, c->vi.vol_id, c->vi.name); c->vi.ubi_num, c->vi.vol_id, c->vi.name);
if (mounted_read_only) if (mounted_read_only)
ubifs_msg("mounted read-only"); ubifs_msg("mounted read-only");
x = (long long)c->main_lebs * c->leb_size; x = (long long)c->main_lebs * c->leb_size;
ubifs_msg("file system size: %lld bytes (%lld KiB, %lld MiB, %d LEBs)", ubifs_msg("file system size: %lld bytes (%lld KiB, %lld MiB, %d "
x, x >> 10, x >> 20, c->main_lebs); "LEBs)", x, x >> 10, x >> 20, c->main_lebs);
x = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes; x = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes;
ubifs_msg("journal size: %lld bytes (%lld KiB, %lld MiB, %d LEBs)", ubifs_msg("journal size: %lld bytes (%lld KiB, %lld MiB, %d "
x, x >> 10, x >> 20, c->log_lebs + c->max_bud_cnt); "LEBs)", x, x >> 10, x >> 20, c->log_lebs + c->max_bud_cnt);
ubifs_msg("default compressor: %s", ubifs_compr_name(c->default_compr)); ubifs_msg("media format: %d (latest is %d)",
ubifs_msg("media format %d, latest format %d",
c->fmt_version, UBIFS_FORMAT_VERSION); c->fmt_version, UBIFS_FORMAT_VERSION);
ubifs_msg("default compressor: %s", ubifs_compr_name(c->default_compr));
ubifs_msg("reserved for root: %llu bytes (%llu KiB)",
c->report_rp_size, c->report_rp_size >> 10);
dbg_msg("compiled on: " __DATE__ " at " __TIME__); dbg_msg("compiled on: " __DATE__ " at " __TIME__);
dbg_msg("min. I/O unit size: %d bytes", c->min_io_size); dbg_msg("min. I/O unit size: %d bytes", c->min_io_size);
dbg_msg("LEB size: %d bytes (%d KiB)", dbg_msg("LEB size: %d bytes (%d KiB)",
c->leb_size, c->leb_size / 1024); c->leb_size, c->leb_size >> 10);
dbg_msg("data journal heads: %d", dbg_msg("data journal heads: %d",
c->jhead_cnt - NONDATA_JHEADS_CNT); c->jhead_cnt - NONDATA_JHEADS_CNT);
dbg_msg("UUID: %02X%02X%02X%02X-%02X%02X" dbg_msg("UUID: %02X%02X%02X%02X-%02X%02X"
...@@ -1282,6 +1349,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) ...@@ -1282,6 +1349,7 @@ static int ubifs_remount_rw(struct ubifs_info *c)
mutex_lock(&c->umount_mutex); mutex_lock(&c->umount_mutex);
c->remounting_rw = 1; c->remounting_rw = 1;
c->always_chk_crc = 1;
/* Check for enough free space */ /* Check for enough free space */
if (ubifs_calc_available(c, c->min_idx_lebs) <= 0) { if (ubifs_calc_available(c, c->min_idx_lebs) <= 0) {
...@@ -1345,20 +1413,20 @@ static int ubifs_remount_rw(struct ubifs_info *c) ...@@ -1345,20 +1413,20 @@ static int ubifs_remount_rw(struct ubifs_info *c)
/* Create background thread */ /* Create background thread */
c->bgt = kthread_create(ubifs_bg_thread, c, c->bgt_name); c->bgt = kthread_create(ubifs_bg_thread, c, c->bgt_name);
if (!c->bgt)
c->bgt = ERR_PTR(-EINVAL);
if (IS_ERR(c->bgt)) { if (IS_ERR(c->bgt)) {
err = PTR_ERR(c->bgt); err = PTR_ERR(c->bgt);
c->bgt = NULL; c->bgt = NULL;
ubifs_err("cannot spawn \"%s\", error %d", ubifs_err("cannot spawn \"%s\", error %d",
c->bgt_name, err); c->bgt_name, err);
return err; goto out;
} }
wake_up_process(c->bgt); wake_up_process(c->bgt);
c->orph_buf = vmalloc(c->leb_size); c->orph_buf = vmalloc(c->leb_size);
if (!c->orph_buf) if (!c->orph_buf) {
return -ENOMEM; err = -ENOMEM;
goto out;
}
/* Check for enough log space */ /* Check for enough log space */
lnum = c->lhead_lnum + 1; lnum = c->lhead_lnum + 1;
...@@ -1385,6 +1453,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) ...@@ -1385,6 +1453,7 @@ static int ubifs_remount_rw(struct ubifs_info *c)
dbg_gen("re-mounted read-write"); dbg_gen("re-mounted read-write");
c->vfs_sb->s_flags &= ~MS_RDONLY; c->vfs_sb->s_flags &= ~MS_RDONLY;
c->remounting_rw = 0; c->remounting_rw = 0;
c->always_chk_crc = 0;
mutex_unlock(&c->umount_mutex); mutex_unlock(&c->umount_mutex);
return 0; return 0;
...@@ -1400,6 +1469,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) ...@@ -1400,6 +1469,7 @@ static int ubifs_remount_rw(struct ubifs_info *c)
c->ileb_buf = NULL; c->ileb_buf = NULL;
ubifs_lpt_free(c, 1); ubifs_lpt_free(c, 1);
c->remounting_rw = 0; c->remounting_rw = 0;
c->always_chk_crc = 0;
mutex_unlock(&c->umount_mutex); mutex_unlock(&c->umount_mutex);
return err; return err;
} }
...@@ -1408,12 +1478,9 @@ static int ubifs_remount_rw(struct ubifs_info *c) ...@@ -1408,12 +1478,9 @@ static int ubifs_remount_rw(struct ubifs_info *c)
* commit_on_unmount - commit the journal when un-mounting. * commit_on_unmount - commit the journal when un-mounting.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
* *
* This function is called during un-mounting and it commits the journal unless * This function is called during un-mounting and re-mounting, and it commits
* the "fast unmount" mode is enabled. It also avoids committing the journal if * the journal unless the "fast unmount" mode is enabled. It also avoids
* it contains too few data. * committing the journal if it contains too few data.
*
* Sometimes recovery requires the journal to be committed at least once, and
* this function takes care about this.
*/ */
static void commit_on_unmount(struct ubifs_info *c) static void commit_on_unmount(struct ubifs_info *c)
{ {
......
...@@ -284,7 +284,7 @@ static struct ubifs_znode *dirty_cow_znode(struct ubifs_info *c, ...@@ -284,7 +284,7 @@ static struct ubifs_znode *dirty_cow_znode(struct ubifs_info *c,
} }
zn = copy_znode(c, znode); zn = copy_znode(c, znode);
if (unlikely(IS_ERR(zn))) if (IS_ERR(zn))
return zn; return zn;
if (zbr->len) { if (zbr->len) {
...@@ -470,6 +470,10 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type, ...@@ -470,6 +470,10 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type,
if (node_len != len) if (node_len != len)
return 0; return 0;
if (type == UBIFS_DATA_NODE && !c->always_chk_crc)
if (c->no_chk_data_crc)
return 0;
crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8);
node_crc = le32_to_cpu(ch->crc); node_crc = le32_to_cpu(ch->crc);
if (crc != node_crc) if (crc != node_crc)
...@@ -1128,7 +1132,7 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, ...@@ -1128,7 +1132,7 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c,
ubifs_assert(znode == c->zroot.znode); ubifs_assert(znode == c->zroot.znode);
znode = dirty_cow_znode(c, &c->zroot); znode = dirty_cow_znode(c, &c->zroot);
} }
if (unlikely(IS_ERR(znode)) || !p) if (IS_ERR(znode) || !p)
break; break;
ubifs_assert(path[p - 1] >= 0); ubifs_assert(path[p - 1] >= 0);
ubifs_assert(path[p - 1] < znode->child_cnt); ubifs_assert(path[p - 1] < znode->child_cnt);
...@@ -1491,6 +1495,289 @@ int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key, ...@@ -1491,6 +1495,289 @@ int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
return err; return err;
} }
/**
* ubifs_tnc_get_bu_keys - lookup keys for bulk-read.
* @c: UBIFS file-system description object
* @bu: bulk-read parameters and results
*
* Lookup consecutive data node keys for the same inode that reside
* consecutively in the same LEB.
*/
int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu)
{
int n, err = 0, lnum = -1, uninitialized_var(offs);
int uninitialized_var(len);
unsigned int block = key_block(c, &bu->key);
struct ubifs_znode *znode;
bu->cnt = 0;
bu->blk_cnt = 0;
bu->eof = 0;
mutex_lock(&c->tnc_mutex);
/* Find first key */
err = ubifs_lookup_level0(c, &bu->key, &znode, &n);
if (err < 0)
goto out;
if (err) {
/* Key found */
len = znode->zbranch[n].len;
/* The buffer must be big enough for at least 1 node */
if (len > bu->buf_len) {
err = -EINVAL;
goto out;
}
/* Add this key */
bu->zbranch[bu->cnt++] = znode->zbranch[n];
bu->blk_cnt += 1;
lnum = znode->zbranch[n].lnum;
offs = ALIGN(znode->zbranch[n].offs + len, 8);
}
while (1) {
struct ubifs_zbranch *zbr;
union ubifs_key *key;
unsigned int next_block;
/* Find next key */
err = tnc_next(c, &znode, &n);
if (err)
goto out;
zbr = &znode->zbranch[n];
key = &zbr->key;
/* See if there is another data key for this file */
if (key_inum(c, key) != key_inum(c, &bu->key) ||
key_type(c, key) != UBIFS_DATA_KEY) {
err = -ENOENT;
goto out;
}
if (lnum < 0) {
/* First key found */
lnum = zbr->lnum;
offs = ALIGN(zbr->offs + zbr->len, 8);
len = zbr->len;
if (len > bu->buf_len) {
err = -EINVAL;
goto out;
}
} else {
/*
* The data nodes must be in consecutive positions in
* the same LEB.
*/
if (zbr->lnum != lnum || zbr->offs != offs)
goto out;
offs += ALIGN(zbr->len, 8);
len = ALIGN(len, 8) + zbr->len;
/* Must not exceed buffer length */
if (len > bu->buf_len)
goto out;
}
/* Allow for holes */
next_block = key_block(c, key);
bu->blk_cnt += (next_block - block - 1);
if (bu->blk_cnt >= UBIFS_MAX_BULK_READ)
goto out;
block = next_block;
/* Add this key */
bu->zbranch[bu->cnt++] = *zbr;
bu->blk_cnt += 1;
/* See if we have room for more */
if (bu->cnt >= UBIFS_MAX_BULK_READ)
goto out;
if (bu->blk_cnt >= UBIFS_MAX_BULK_READ)
goto out;
}
out:
if (err == -ENOENT) {
bu->eof = 1;
err = 0;
}
bu->gc_seq = c->gc_seq;
mutex_unlock(&c->tnc_mutex);
if (err)
return err;
/*
* An enormous hole could cause bulk-read to encompass too many
* page cache pages, so limit the number here.
*/
if (bu->blk_cnt > UBIFS_MAX_BULK_READ)
bu->blk_cnt = UBIFS_MAX_BULK_READ;
/*
* Ensure that bulk-read covers a whole number of page cache
* pages.
*/
if (UBIFS_BLOCKS_PER_PAGE == 1 ||
!(bu->blk_cnt & (UBIFS_BLOCKS_PER_PAGE - 1)))
return 0;
if (bu->eof) {
/* At the end of file we can round up */
bu->blk_cnt += UBIFS_BLOCKS_PER_PAGE - 1;
return 0;
}
/* Exclude data nodes that do not make up a whole page cache page */
block = key_block(c, &bu->key) + bu->blk_cnt;
block &= ~(UBIFS_BLOCKS_PER_PAGE - 1);
while (bu->cnt) {
if (key_block(c, &bu->zbranch[bu->cnt - 1].key) < block)
break;
bu->cnt -= 1;
}
return 0;
}
/**
* read_wbuf - bulk-read from a LEB with a wbuf.
* @wbuf: wbuf that may overlap the read
* @buf: buffer into which to read
* @len: read length
* @lnum: LEB number from which to read
* @offs: offset from which to read
*
* This functions returns %0 on success or a negative error code on failure.
*/
static int read_wbuf(struct ubifs_wbuf *wbuf, void *buf, int len, int lnum,
int offs)
{
const struct ubifs_info *c = wbuf->c;
int rlen, overlap;
dbg_io("LEB %d:%d, length %d", lnum, offs, len);
ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
ubifs_assert(!(offs & 7) && offs < c->leb_size);
ubifs_assert(offs + len <= c->leb_size);
spin_lock(&wbuf->lock);
overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs);
if (!overlap) {
/* We may safely unlock the write-buffer and read the data */
spin_unlock(&wbuf->lock);
return ubi_read(c->ubi, lnum, buf, offs, len);
}
/* Don't read under wbuf */
rlen = wbuf->offs - offs;
if (rlen < 0)
rlen = 0;
/* Copy the rest from the write-buffer */
memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen);
spin_unlock(&wbuf->lock);
if (rlen > 0)
/* Read everything that goes before write-buffer */
return ubi_read(c->ubi, lnum, buf, offs, rlen);
return 0;
}
/**
* validate_data_node - validate data nodes for bulk-read.
* @c: UBIFS file-system description object
* @buf: buffer containing data node to validate
* @zbr: zbranch of data node to validate
*
* This functions returns %0 on success or a negative error code on failure.
*/
static int validate_data_node(struct ubifs_info *c, void *buf,
struct ubifs_zbranch *zbr)
{
union ubifs_key key1;
struct ubifs_ch *ch = buf;
int err, len;
if (ch->node_type != UBIFS_DATA_NODE) {
ubifs_err("bad node type (%d but expected %d)",
ch->node_type, UBIFS_DATA_NODE);
goto out_err;
}
err = ubifs_check_node(c, buf, zbr->lnum, zbr->offs, 0, 0);
if (err) {
ubifs_err("expected node type %d", UBIFS_DATA_NODE);
goto out;
}
len = le32_to_cpu(ch->len);
if (len != zbr->len) {
ubifs_err("bad node length %d, expected %d", len, zbr->len);
goto out_err;
}
/* Make sure the key of the read node is correct */
key_read(c, buf + UBIFS_KEY_OFFSET, &key1);
if (!keys_eq(c, &zbr->key, &key1)) {
ubifs_err("bad key in node at LEB %d:%d",
zbr->lnum, zbr->offs);
dbg_tnc("looked for key %s found node's key %s",
DBGKEY(&zbr->key), DBGKEY1(&key1));
goto out_err;
}
return 0;
out_err:
err = -EINVAL;
out:
ubifs_err("bad node at LEB %d:%d", zbr->lnum, zbr->offs);
dbg_dump_node(c, buf);
dbg_dump_stack();
return err;
}
/**
* ubifs_tnc_bulk_read - read a number of data nodes in one go.
* @c: UBIFS file-system description object
* @bu: bulk-read parameters and results
*
* This functions reads and validates the data nodes that were identified by the
* 'ubifs_tnc_get_bu_keys()' function. This functions returns %0 on success,
* -EAGAIN to indicate a race with GC, or another negative error code on
* failure.
*/
int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu)
{
int lnum = bu->zbranch[0].lnum, offs = bu->zbranch[0].offs, len, err, i;
struct ubifs_wbuf *wbuf;
void *buf;
len = bu->zbranch[bu->cnt - 1].offs;
len += bu->zbranch[bu->cnt - 1].len - offs;
if (len > bu->buf_len) {
ubifs_err("buffer too small %d vs %d", bu->buf_len, len);
return -EINVAL;
}
/* Do the read */
wbuf = ubifs_get_wbuf(c, lnum);
if (wbuf)
err = read_wbuf(wbuf, bu->buf, len, lnum, offs);
else
err = ubi_read(c->ubi, lnum, bu->buf, offs, len);
/* Check for a race with GC */
if (maybe_leb_gced(c, lnum, bu->gc_seq))
return -EAGAIN;
if (err && err != -EBADMSG) {
ubifs_err("failed to read from LEB %d:%d, error %d",
lnum, offs, err);
dbg_dump_stack();
dbg_tnc("key %s", DBGKEY(&bu->key));
return err;
}
/* Validate the nodes read */
buf = bu->buf;
for (i = 0; i < bu->cnt; i++) {
err = validate_data_node(c, buf, &bu->zbranch[i]);
if (err)
return err;
buf = buf + ALIGN(bu->zbranch[i].len, 8);
}
return 0;
}
/** /**
* do_lookup_nm- look up a "hashed" node. * do_lookup_nm- look up a "hashed" node.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
...@@ -1675,7 +1962,7 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode, ...@@ -1675,7 +1962,7 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode,
{ {
struct ubifs_znode *zn, *zi, *zp; struct ubifs_znode *zn, *zi, *zp;
int i, keep, move, appending = 0; int i, keep, move, appending = 0;
union ubifs_key *key = &zbr->key; union ubifs_key *key = &zbr->key, *key1;
ubifs_assert(n >= 0 && n <= c->fanout); ubifs_assert(n >= 0 && n <= c->fanout);
...@@ -1716,20 +2003,33 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode, ...@@ -1716,20 +2003,33 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode,
zn->level = znode->level; zn->level = znode->level;
/* Decide where to split */ /* Decide where to split */
if (znode->level == 0 && n == c->fanout && if (znode->level == 0 && key_type(c, key) == UBIFS_DATA_KEY) {
key_type(c, key) == UBIFS_DATA_KEY) { /* Try not to split consecutive data keys */
union ubifs_key *key1; if (n == c->fanout) {
key1 = &znode->zbranch[n - 1].key;
/* if (key_inum(c, key1) == key_inum(c, key) &&
* If this is an inode which is being appended - do not split key_type(c, key1) == UBIFS_DATA_KEY)
* it because no other zbranches can be inserted between appending = 1;
* zbranches of consecutive data nodes anyway. } else
*/ goto check_split;
key1 = &znode->zbranch[n - 1].key; } else if (appending && n != c->fanout) {
if (key_inum(c, key1) == key_inum(c, key) && /* Try not to split consecutive data keys */
key_type(c, key1) == UBIFS_DATA_KEY && appending = 0;
key_block(c, key1) == key_block(c, key) - 1) check_split:
appending = 1; if (n >= (c->fanout + 1) / 2) {
key1 = &znode->zbranch[0].key;
if (key_inum(c, key1) == key_inum(c, key) &&
key_type(c, key1) == UBIFS_DATA_KEY) {
key1 = &znode->zbranch[n].key;
if (key_inum(c, key1) != key_inum(c, key) ||
key_type(c, key1) != UBIFS_DATA_KEY) {
keep = n;
move = c->fanout - keep;
zi = znode;
goto do_split;
}
}
}
} }
if (appending) { if (appending) {
...@@ -1759,6 +2059,8 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode, ...@@ -1759,6 +2059,8 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode,
zbr->znode->parent = zn; zbr->znode->parent = zn;
} }
do_split:
__set_bit(DIRTY_ZNODE, &zn->flags); __set_bit(DIRTY_ZNODE, &zn->flags);
atomic_long_inc(&c->dirty_zn_cnt); atomic_long_inc(&c->dirty_zn_cnt);
...@@ -1785,14 +2087,11 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode, ...@@ -1785,14 +2087,11 @@ static int tnc_insert(struct ubifs_info *c, struct ubifs_znode *znode,
/* Insert new znode (produced by spitting) into the parent */ /* Insert new znode (produced by spitting) into the parent */
if (zp) { if (zp) {
i = n; if (n == 0 && zi == znode && znode->iip == 0)
correct_parent_keys(c, znode);
/* Locate insertion point */ /* Locate insertion point */
n = znode->iip + 1; n = znode->iip + 1;
if (appending && n != c->fanout)
appending = 0;
if (i == 0 && zi == znode && znode->iip == 0)
correct_parent_keys(c, znode);
/* Tail recursion */ /* Tail recursion */
zbr->key = zn->zbranch[0].key; zbr->key = zn->zbranch[0].key;
......
...@@ -480,8 +480,8 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, ...@@ -480,8 +480,8 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
} }
/* Make sure the key of the read node is correct */ /* Make sure the key of the read node is correct */
key_read(c, key, &key1); key_read(c, node + UBIFS_KEY_OFFSET, &key1);
if (memcmp(node + UBIFS_KEY_OFFSET, &key1, c->key_len)) { if (!keys_eq(c, key, &key1)) {
ubifs_err("bad key in node at LEB %d:%d", ubifs_err("bad key in node at LEB %d:%d",
zbr->lnum, zbr->offs); zbr->lnum, zbr->offs);
dbg_tnc("looked for key %s found node's key %s", dbg_tnc("looked for key %s found node's key %s",
......
...@@ -75,7 +75,6 @@ ...@@ -75,7 +75,6 @@
*/ */
#define UBIFS_BLOCK_SIZE 4096 #define UBIFS_BLOCK_SIZE 4096
#define UBIFS_BLOCK_SHIFT 12 #define UBIFS_BLOCK_SHIFT 12
#define UBIFS_BLOCK_MASK 0x00000FFF
/* UBIFS padding byte pattern (must not be first or last byte of node magic) */ /* UBIFS padding byte pattern (must not be first or last byte of node magic) */
#define UBIFS_PADDING_BYTE 0xCE #define UBIFS_PADDING_BYTE 0xCE
......
...@@ -142,6 +142,9 @@ ...@@ -142,6 +142,9 @@
/* Maximum expected tree height for use by bottom_up_buf */ /* Maximum expected tree height for use by bottom_up_buf */
#define BOTTOM_UP_HEIGHT 64 #define BOTTOM_UP_HEIGHT 64
/* Maximum number of data nodes to bulk-read */
#define UBIFS_MAX_BULK_READ 32
/* /*
* Lockdep classes for UBIFS inode @ui_mutex. * Lockdep classes for UBIFS inode @ui_mutex.
*/ */
...@@ -328,9 +331,10 @@ struct ubifs_gced_idx_leb { ...@@ -328,9 +331,10 @@ struct ubifs_gced_idx_leb {
* this inode * this inode
* @dirty: non-zero if the inode is dirty * @dirty: non-zero if the inode is dirty
* @xattr: non-zero if this is an extended attribute inode * @xattr: non-zero if this is an extended attribute inode
* @bulk_read: non-zero if bulk-read should be used
* @ui_mutex: serializes inode write-back with the rest of VFS operations, * @ui_mutex: serializes inode write-back with the rest of VFS operations,
* serializes "clean <-> dirty" state changes, protects @dirty, * serializes "clean <-> dirty" state changes, serializes bulk-read,
* @ui_size, and @xattr_size * protects @dirty, @bulk_read, @ui_size, and @xattr_size
* @ui_lock: protects @synced_i_size * @ui_lock: protects @synced_i_size
* @synced_i_size: synchronized size of inode, i.e. the value of inode size * @synced_i_size: synchronized size of inode, i.e. the value of inode size
* currently stored on the flash; used only for regular file * currently stored on the flash; used only for regular file
...@@ -338,6 +342,8 @@ struct ubifs_gced_idx_leb { ...@@ -338,6 +342,8 @@ struct ubifs_gced_idx_leb {
* @ui_size: inode size used by UBIFS when writing to flash * @ui_size: inode size used by UBIFS when writing to flash
* @flags: inode flags (@UBIFS_COMPR_FL, etc) * @flags: inode flags (@UBIFS_COMPR_FL, etc)
* @compr_type: default compression type used for this inode * @compr_type: default compression type used for this inode
* @last_page_read: page number of last page read (for bulk read)
* @read_in_a_row: number of consecutive pages read in a row (for bulk read)
* @data_len: length of the data attached to the inode * @data_len: length of the data attached to the inode
* @data: inode's data * @data: inode's data
* *
...@@ -379,12 +385,15 @@ struct ubifs_inode { ...@@ -379,12 +385,15 @@ struct ubifs_inode {
unsigned int xattr_names; unsigned int xattr_names;
unsigned int dirty:1; unsigned int dirty:1;
unsigned int xattr:1; unsigned int xattr:1;
unsigned int bulk_read:1;
struct mutex ui_mutex; struct mutex ui_mutex;
spinlock_t ui_lock; spinlock_t ui_lock;
loff_t synced_i_size; loff_t synced_i_size;
loff_t ui_size; loff_t ui_size;
int flags; int flags;
int compr_type; int compr_type;
pgoff_t last_page_read;
pgoff_t read_in_a_row;
int data_len; int data_len;
void *data; void *data;
}; };
...@@ -698,8 +707,8 @@ struct ubifs_jhead { ...@@ -698,8 +707,8 @@ struct ubifs_jhead {
* struct ubifs_zbranch - key/coordinate/length branch stored in znodes. * struct ubifs_zbranch - key/coordinate/length branch stored in znodes.
* @key: key * @key: key
* @znode: znode address in memory * @znode: znode address in memory
* @lnum: LEB number of the indexing node * @lnum: LEB number of the target node (indexing node or data node)
* @offs: offset of the indexing node within @lnum * @offs: target node offset within @lnum
* @len: target node length * @len: target node length
*/ */
struct ubifs_zbranch { struct ubifs_zbranch {
...@@ -743,6 +752,28 @@ struct ubifs_znode { ...@@ -743,6 +752,28 @@ struct ubifs_znode {
struct ubifs_zbranch zbranch[]; struct ubifs_zbranch zbranch[];
}; };
/**
* struct bu_info - bulk-read information
* @key: first data node key
* @zbranch: zbranches of data nodes to bulk read
* @buf: buffer to read into
* @buf_len: buffer length
* @gc_seq: GC sequence number to detect races with GC
* @cnt: number of data nodes for bulk read
* @blk_cnt: number of data blocks including holes
* @oef: end of file reached
*/
struct bu_info {
union ubifs_key key;
struct ubifs_zbranch zbranch[UBIFS_MAX_BULK_READ];
void *buf;
int buf_len;
int gc_seq;
int cnt;
int blk_cnt;
int eof;
};
/** /**
* struct ubifs_node_range - node length range description data structure. * struct ubifs_node_range - node length range description data structure.
* @len: fixed node length * @len: fixed node length
...@@ -862,9 +893,13 @@ struct ubifs_orphan { ...@@ -862,9 +893,13 @@ struct ubifs_orphan {
/** /**
* struct ubifs_mount_opts - UBIFS-specific mount options information. * struct ubifs_mount_opts - UBIFS-specific mount options information.
* @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast) * @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast)
* @bulk_read: enable bulk-reads
* @chk_data_crc: check CRCs when reading data nodes
*/ */
struct ubifs_mount_opts { struct ubifs_mount_opts {
unsigned int unmount_mode:2; unsigned int unmount_mode:2;
unsigned int bulk_read:2;
unsigned int chk_data_crc:2;
}; };
/** /**
...@@ -905,13 +940,12 @@ struct ubifs_mount_opts { ...@@ -905,13 +940,12 @@ struct ubifs_mount_opts {
* @cmt_state: commit state * @cmt_state: commit state
* @cs_lock: commit state lock * @cs_lock: commit state lock
* @cmt_wq: wait queue to sleep on if the log is full and a commit is running * @cmt_wq: wait queue to sleep on if the log is full and a commit is running
*
* @fast_unmount: do not run journal commit before un-mounting * @fast_unmount: do not run journal commit before un-mounting
* @big_lpt: flag that LPT is too big to write whole during commit * @big_lpt: flag that LPT is too big to write whole during commit
* @check_lpt_free: flag that indicates LPT GC may be needed * @no_chk_data_crc: do not check CRCs when reading data nodes (except during
* @nospace: non-zero if the file-system does not have flash space (used as * recovery)
* optimization) * @bulk_read: enable bulk-reads
* @nospace_rp: the same as @nospace, but additionally means that even reserved
* pool is full
* *
* @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and * @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and
* @calc_idx_sz * @calc_idx_sz
...@@ -935,6 +969,7 @@ struct ubifs_mount_opts { ...@@ -935,6 +969,7 @@ struct ubifs_mount_opts {
* @mst_node: master node * @mst_node: master node
* @mst_offs: offset of valid master node * @mst_offs: offset of valid master node
* @mst_mutex: protects the master node area, @mst_node, and @mst_offs * @mst_mutex: protects the master node area, @mst_node, and @mst_offs
* @bulk_read_buf_size: buffer size for bulk-reads
* *
* @log_lebs: number of logical eraseblocks in the log * @log_lebs: number of logical eraseblocks in the log
* @log_bytes: log size in bytes * @log_bytes: log size in bytes
...@@ -977,12 +1012,17 @@ struct ubifs_mount_opts { ...@@ -977,12 +1012,17 @@ struct ubifs_mount_opts {
* but which still have to be taken into account because * but which still have to be taken into account because
* the index has not been committed so far * the index has not been committed so far
* @space_lock: protects @budg_idx_growth, @budg_data_growth, @budg_dd_growth, * @space_lock: protects @budg_idx_growth, @budg_data_growth, @budg_dd_growth,
* @budg_uncommited_idx, @min_idx_lebs, @old_idx_sz, and @lst; * @budg_uncommited_idx, @min_idx_lebs, @old_idx_sz, @lst,
* @nospace, and @nospace_rp;
* @min_idx_lebs: minimum number of LEBs required for the index * @min_idx_lebs: minimum number of LEBs required for the index
* @old_idx_sz: size of index on flash * @old_idx_sz: size of index on flash
* @calc_idx_sz: temporary variable which is used to calculate new index size * @calc_idx_sz: temporary variable which is used to calculate new index size
* (contains accurate new index size at end of TNC commit start) * (contains accurate new index size at end of TNC commit start)
* @lst: lprops statistics * @lst: lprops statistics
* @nospace: non-zero if the file-system does not have flash space (used as
* optimization)
* @nospace_rp: the same as @nospace, but additionally means that even reserved
* pool is full
* *
* @page_budget: budget for a page * @page_budget: budget for a page
* @inode_budget: budget for an inode * @inode_budget: budget for an inode
...@@ -1061,6 +1101,7 @@ struct ubifs_mount_opts { ...@@ -1061,6 +1101,7 @@ struct ubifs_mount_opts {
* @lpt_drty_flgs: dirty flags for LPT special nodes e.g. ltab * @lpt_drty_flgs: dirty flags for LPT special nodes e.g. ltab
* @dirty_nn_cnt: number of dirty nnodes * @dirty_nn_cnt: number of dirty nnodes
* @dirty_pn_cnt: number of dirty pnodes * @dirty_pn_cnt: number of dirty pnodes
* @check_lpt_free: flag that indicates LPT GC may be needed
* @lpt_sz: LPT size * @lpt_sz: LPT size
* @lpt_nod_buf: buffer for an on-flash nnode or pnode * @lpt_nod_buf: buffer for an on-flash nnode or pnode
* @lpt_buf: buffer of LEB size used by LPT * @lpt_buf: buffer of LEB size used by LPT
...@@ -1102,6 +1143,7 @@ struct ubifs_mount_opts { ...@@ -1102,6 +1143,7 @@ struct ubifs_mount_opts {
* @rcvrd_mst_node: recovered master node to write when mounting ro to rw * @rcvrd_mst_node: recovered master node to write when mounting ro to rw
* @size_tree: inode size information for recovery * @size_tree: inode size information for recovery
* @remounting_rw: set while remounting from ro to rw (sb flags have MS_RDONLY) * @remounting_rw: set while remounting from ro to rw (sb flags have MS_RDONLY)
* @always_chk_crc: always check CRCs (while mounting and remounting rw)
* @mount_opts: UBIFS-specific mount options * @mount_opts: UBIFS-specific mount options
* *
* @dbg_buf: a buffer of LEB size used for debugging purposes * @dbg_buf: a buffer of LEB size used for debugging purposes
...@@ -1146,11 +1188,11 @@ struct ubifs_info { ...@@ -1146,11 +1188,11 @@ struct ubifs_info {
int cmt_state; int cmt_state;
spinlock_t cs_lock; spinlock_t cs_lock;
wait_queue_head_t cmt_wq; wait_queue_head_t cmt_wq;
unsigned int fast_unmount:1; unsigned int fast_unmount:1;
unsigned int big_lpt:1; unsigned int big_lpt:1;
unsigned int check_lpt_free:1; unsigned int no_chk_data_crc:1;
unsigned int nospace:1; unsigned int bulk_read:1;
unsigned int nospace_rp:1;
struct mutex tnc_mutex; struct mutex tnc_mutex;
struct ubifs_zbranch zroot; struct ubifs_zbranch zroot;
...@@ -1175,6 +1217,7 @@ struct ubifs_info { ...@@ -1175,6 +1217,7 @@ struct ubifs_info {
struct ubifs_mst_node *mst_node; struct ubifs_mst_node *mst_node;
int mst_offs; int mst_offs;
struct mutex mst_mutex; struct mutex mst_mutex;
int bulk_read_buf_size;
int log_lebs; int log_lebs;
long long log_bytes; long long log_bytes;
...@@ -1218,6 +1261,8 @@ struct ubifs_info { ...@@ -1218,6 +1261,8 @@ struct ubifs_info {
unsigned long long old_idx_sz; unsigned long long old_idx_sz;
unsigned long long calc_idx_sz; unsigned long long calc_idx_sz;
struct ubifs_lp_stats lst; struct ubifs_lp_stats lst;
unsigned int nospace:1;
unsigned int nospace_rp:1;
int page_budget; int page_budget;
int inode_budget; int inode_budget;
...@@ -1294,6 +1339,7 @@ struct ubifs_info { ...@@ -1294,6 +1339,7 @@ struct ubifs_info {
int lpt_drty_flgs; int lpt_drty_flgs;
int dirty_nn_cnt; int dirty_nn_cnt;
int dirty_pn_cnt; int dirty_pn_cnt;
int check_lpt_free;
long long lpt_sz; long long lpt_sz;
void *lpt_nod_buf; void *lpt_nod_buf;
void *lpt_buf; void *lpt_buf;
...@@ -1335,6 +1381,7 @@ struct ubifs_info { ...@@ -1335,6 +1381,7 @@ struct ubifs_info {
struct ubifs_mst_node *rcvrd_mst_node; struct ubifs_mst_node *rcvrd_mst_node;
struct rb_root size_tree; struct rb_root size_tree;
int remounting_rw; int remounting_rw;
int always_chk_crc;
struct ubifs_mount_opts mount_opts; struct ubifs_mount_opts mount_opts;
#ifdef CONFIG_UBIFS_FS_DEBUG #ifdef CONFIG_UBIFS_FS_DEBUG
...@@ -1347,6 +1394,12 @@ struct ubifs_info { ...@@ -1347,6 +1394,12 @@ struct ubifs_info {
unsigned long fail_timeout; unsigned long fail_timeout;
unsigned int fail_cnt; unsigned int fail_cnt;
unsigned int fail_cnt_max; unsigned int fail_cnt_max;
long long chk_lpt_sz;
long long chk_lpt_sz2;
long long chk_lpt_wastage;
int chk_lpt_lebs;
int new_nhead_lnum;
int new_nhead_offs;
#endif #endif
}; };
...@@ -1377,7 +1430,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, ...@@ -1377,7 +1430,7 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum, int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum,
int offs, int dtype); int offs, int dtype);
int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
int offs, int quiet); int offs, int quiet, int chk_crc);
void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad); void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad);
void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last); void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
int ubifs_io_init(struct ubifs_info *c); int ubifs_io_init(struct ubifs_info *c);
...@@ -1490,6 +1543,8 @@ void destroy_old_idx(struct ubifs_info *c); ...@@ -1490,6 +1543,8 @@ void destroy_old_idx(struct ubifs_info *c);
int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level, int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level,
int lnum, int offs); int lnum, int offs);
int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode); int insert_old_idx_znode(struct ubifs_info *c, struct ubifs_znode *znode);
int ubifs_tnc_get_bu_keys(struct ubifs_info *c, struct bu_info *bu);
int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu);
/* tnc_misc.c */ /* tnc_misc.c */
struct ubifs_znode *ubifs_tnc_levelorder_next(struct ubifs_znode *zr, struct ubifs_znode *ubifs_tnc_levelorder_next(struct ubifs_znode *zr,
...@@ -1586,12 +1641,10 @@ int ubifs_lpt_post_commit(struct ubifs_info *c); ...@@ -1586,12 +1641,10 @@ int ubifs_lpt_post_commit(struct ubifs_info *c);
void ubifs_lpt_free(struct ubifs_info *c, int wr_only); void ubifs_lpt_free(struct ubifs_info *c, int wr_only);
/* lprops.c */ /* lprops.c */
void ubifs_get_lprops(struct ubifs_info *c);
const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
const struct ubifs_lprops *lp, const struct ubifs_lprops *lp,
int free, int dirty, int flags, int free, int dirty, int flags,
int idx_gc_cnt); int idx_gc_cnt);
void ubifs_release_lprops(struct ubifs_info *c);
void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *stats); void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *stats);
void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops, void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
int cat); int cat);
......
...@@ -446,7 +446,7 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size) ...@@ -446,7 +446,7 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
int type; int type;
xent = ubifs_tnc_next_ent(c, &key, &nm); xent = ubifs_tnc_next_ent(c, &key, &nm);
if (unlikely(IS_ERR(xent))) { if (IS_ERR(xent)) {
err = PTR_ERR(xent); err = PTR_ERR(xent);
break; break;
} }
......
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