pgoff_t start_fofs, end_fofs;
block_t start_blkaddr;
- if (is_inode_flag_set(fi, FI_NO_EXTENT))
- return false;
-
read_lock(&fi->ext_lock);
if (fi->ext.len == 0) {
read_unlock(&fi->ext_lock);
block_t start_blkaddr, end_blkaddr;
int need_update = true;
- if (is_inode_flag_set(fi, FI_NO_EXTENT))
- return false;
-
write_lock(&fi->ext_lock);
start_fofs = fi->ext.fofs;
rb_erase(&en->rb_node, &et->root);
et->count--;
atomic_dec(&sbi->total_ext_node);
+
+ if (et->cached_en == en)
+ et->cached_en = NULL;
}
static struct extent_node *__lookup_extent_tree(struct extent_tree *et,
struct rb_node *node = et->root.rb_node;
struct extent_node *en;
+ if (et->cached_en) {
+ struct extent_info *cei = &et->cached_en->ei;
+
+ if (cei->fofs <= fofs && cei->fofs + cei->len > fofs)
+ return et->cached_en;
+ }
+
while (node) {
en = rb_entry(node, struct extent_node, rb_node);
- if (fofs < en->ei.fofs)
+ if (fofs < en->ei.fofs) {
node = node->rb_left;
- else if (fofs >= en->ei.fofs + en->ei.len)
+ } else if (fofs >= en->ei.fofs + en->ei.len) {
node = node->rb_right;
- else
+ } else {
+ et->cached_en = en;
return en;
+ }
}
return NULL;
}
struct extent_tree *et;
struct extent_node *en;
- if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
- return false;
+ trace_f2fs_lookup_extent_tree_start(inode, pgofs);
down_read(&sbi->extent_tree_lock);
et = radix_tree_lookup(&sbi->extent_tree_root, inode->i_ino);
stat_inc_total_hit(sbi->sb);
read_unlock(&et->lock);
+ trace_f2fs_lookup_extent_tree_end(inode, pgofs, en);
+
atomic_dec(&et->refcount);
return en ? true : false;
}
struct extent_info ei, dei;
unsigned int endofs;
- if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
- return;
+ trace_f2fs_update_extent_tree(inode, fofs, blkaddr);
down_write(&sbi->extent_tree_lock);
et = radix_tree_lookup(&sbi->extent_tree_root, ino);
memset(et, 0, sizeof(struct extent_tree));
et->ino = ino;
et->root = RB_ROOT;
+ et->cached_en = NULL;
rwlock_init(&et->lock);
atomic_set(&et->refcount, 0);
et->count = 0;
struct radix_tree_iter iter;
void **slot;
unsigned int found;
+ unsigned int node_cnt = 0, tree_cnt = 0;
+
+ if (!test_opt(sbi, EXTENT_CACHE))
+ return;
if (available_free_memory(sbi, EXTENT_CACHE))
return;
atomic_inc(&et->refcount);
write_lock(&et->lock);
- __free_extent_tree(sbi, et, false);
+ node_cnt += __free_extent_tree(sbi, et, false);
write_unlock(&et->lock);
atomic_dec(&et->refcount);
}
radix_tree_delete(&sbi->extent_tree_root, et->ino);
kmem_cache_free(extent_tree_slab, et);
sbi->total_ext_tree--;
+ tree_cnt++;
}
}
up_write(&sbi->extent_tree_lock);
+
+ trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt);
}
void f2fs_destroy_extent_tree(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct extent_tree *et;
+ unsigned int node_cnt = 0;
+
+ if (!test_opt(sbi, EXTENT_CACHE))
+ return;
down_read(&sbi->extent_tree_lock);
et = radix_tree_lookup(&sbi->extent_tree_root, inode->i_ino);
/* free all extent info belong to this extent tree */
write_lock(&et->lock);
- __free_extent_tree(sbi, et, true);
+ node_cnt = __free_extent_tree(sbi, et, true);
write_unlock(&et->lock);
atomic_dec(&et->refcount);
sbi->total_ext_tree--;
up_write(&sbi->extent_tree_lock);
out:
+ trace_f2fs_destroy_extent_tree(inode, node_cnt);
return;
}
static bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs,
struct extent_info *ei)
{
+ if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
+ return false;
+
+ if (test_opt(F2FS_I_SB(inode), EXTENT_CACHE))
+ return f2fs_lookup_extent_tree(inode, pgofs, ei);
+
return lookup_extent_info(inode, pgofs, ei);
}
/* Update the page address in the parent node */
__set_data_blkaddr(dn);
+ if (is_inode_flag_set(fi, FI_NO_EXTENT))
+ return;
+
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
dn->ofs_in_node;
+ if (test_opt(F2FS_I_SB(dn->inode), EXTENT_CACHE))
+ return f2fs_update_extent_tree(dn->inode, fofs,
+ dn->data_blkaddr);
+
if (update_extent_info(dn->inode, fofs, dn->data_blkaddr))
sync_inode_page(dn);
}
struct address_space *mapping = inode->i_mapping;
struct dnode_of_data dn;
struct page *page;
+ struct extent_info ei;
int err;
struct f2fs_io_info fio = {
.type = DATA,
.rw = sync ? READ_SYNC : READA,
};
+ /*
+ * If sync is false, it needs to check its block allocation.
+ * This is need and triggered by two flows:
+ * gc and truncate_partial_data_page.
+ */
+ if (!sync)
+ goto search;
+
page = find_get_page(mapping, index);
if (page && PageUptodate(page))
return page;
f2fs_put_page(page, 0);
+search:
+ if (f2fs_lookup_extent_cache(inode, index, &ei)) {
+ dn.data_blkaddr = ei.blk + index - ei.fofs;
+ goto got_it;
+ }
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (unlikely(dn.data_blkaddr == NEW_ADDR))
return ERR_PTR(-EINVAL);
+got_it:
page = grab_cache_page(mapping, index);
if (!page)
return ERR_PTR(-ENOMEM);
struct address_space *mapping = inode->i_mapping;
struct dnode_of_data dn;
struct page *page;
+ struct extent_info ei;
int err;
struct f2fs_io_info fio = {
.type = DATA,
if (!page)
return ERR_PTR(-ENOMEM);
+ if (f2fs_lookup_extent_cache(inode, index, &ei)) {
+ dn.data_blkaddr = ei.blk + index - ei.fofs;
+ goto got_it;
+ }
+
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err) {
return ERR_PTR(-ENOENT);
}
+got_it:
if (PageUptodate(page))
return page;
fio->blk_addr = dn.data_blkaddr;
/* This page is already truncated */
- if (fio->blk_addr == NULL_ADDR)
+ if (fio->blk_addr == NULL_ADDR) {
+ ClearPageUptodate(page);
goto out_writepage;
+ }
set_page_writeback(page);
clear_cold_data(page);
out:
inode_dec_dirty_pages(inode);
+ if (err)
+ ClearPageUptodate(page);
unlock_page(page);
if (need_balance_fs)
f2fs_balance_fs(sbi);