Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 116195
b: refs/heads/master
c: 4793e7c
h: refs/heads/master
i:
  116193: 9ed1499
  116191: 189ce53
v: v3
  • Loading branch information
Adrian Hunter authored and Artem Bityutskiy committed Sep 30, 2008
1 parent 5140664 commit 2ca1983
Show file tree
Hide file tree
Showing 7 changed files with 630 additions and 4 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a70948b564e9f6cb81115c606d46f5b74a77b7c2
refs/heads/master: 4793e7c5e1c88382ead18db5ca072bac54467318
3 changes: 3 additions & 0 deletions trunk/Documentation/filesystems/ubifs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ norm_unmount (*) commit on unmount; the journal is committed
fast_unmount do not commit on unmount; this option makes
unmount faster, but the next mount slower
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


Quick usage instructions
Expand Down
248 changes: 248 additions & 0 deletions trunk/fs/ubifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,8 +577,256 @@ static int ubifs_write_end(struct file *file, struct address_space *mapping,
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 = 1, 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 + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
if (!i_size || page->index > end_index) {
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 ||
key_block(c, &bu->zbranch[nn].key) != page_block)
memset(addr, 0, UBIFS_BLOCK_SIZE);
else {
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;
hole = 0;
read = (i << UBIFS_BLOCK_SHIFT) + len;
}
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 < 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)
{
if (ubifs_bulk_read(page))
return 0;
do_readpage(page);
unlock_page(page);
return 0;
Expand Down
22 changes: 21 additions & 1 deletion trunk/fs/ubifs/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ static inline void key_copy(const struct ubifs_info *c,
* @key2: the second key to compare
*
* 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,
const union ubifs_key *key1,
Expand All @@ -502,6 +502,26 @@ static inline int keys_cmp(const struct ubifs_info *c,
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.
* @c: UBIFS file-system description object
Expand Down
31 changes: 31 additions & 0 deletions trunk/fs/ubifs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ static int ubifs_show_options(struct seq_file *s, struct vfsmount *mnt)
else if (c->mount_opts.unmount_mode == 1)
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");

return 0;
}

Expand Down Expand Up @@ -538,6 +543,18 @@ static int init_constants_early(struct ubifs_info *c)
* calculations when reporting free space.
*/
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;
}

Expand Down Expand Up @@ -840,17 +857,23 @@ static int check_volume_empty(struct ubifs_info *c)
*
* Opt_fast_unmount: do not 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_err: just end of array marker
*/
enum {
Opt_fast_unmount,
Opt_norm_unmount,
Opt_bulk_read,
Opt_no_bulk_read,
Opt_err,
};

static match_table_t tokens = {
{Opt_fast_unmount, "fast_unmount"},
{Opt_norm_unmount, "norm_unmount"},
{Opt_bulk_read, "bulk_read"},
{Opt_no_bulk_read, "no_bulk_read"},
{Opt_err, NULL},
};

Expand Down Expand Up @@ -888,6 +911,14 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options,
c->mount_opts.unmount_mode = 1;
c->fast_unmount = 0;
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;
default:
ubifs_err("unrecognized mount option \"%s\" "
"or missing value", p);
Expand Down
Loading

0 comments on commit 2ca1983

Please sign in to comment.