summaryrefslogtreecommitdiffstats
path: root/fs/f2fs/extent_cache.c
diff options
context:
space:
mode:
authorFan Li <fanofcode.li@samsung.com>2015-07-15 12:05:17 +0200
committerJaegeuk Kim <jaegeuk@kernel.org>2015-08-05 17:08:08 +0200
commit0f825ee6e873ac0daf5394c5ec76ca2f3d540370 (patch)
treefa2380aea19fb799dd9f31810d019a4badd24f4a /fs/f2fs/extent_cache.c
parentf2fs: callers take care of the page from bio error (diff)
downloadlinux-0f825ee6e873ac0daf5394c5ec76ca2f3d540370.tar.xz
linux-0f825ee6e873ac0daf5394c5ec76ca2f3d540370.zip
f2fs: add new interfaces for extent tree
Add a lookup and a insertion interface for extent tree. The new lookup return the insert position and the prev/next extents closest to the offset we lookup when find no match. The new insertion uses above parameters to improve performance. There are three possible insertions after the lookup in f2fs_update_extent_tree, two of them insert parts of removed extent back to tree, since no merge happens during this process, new insertion skips the merge check in this scanario; the another insertion inserts a new extent to tree, new insertion uses prev/next extent and insert position to insert this extent directly, and save the time of searching down the tree. As long as tree remains unchanged between lookup and insertion, this would work fine. And the new lookup would be useful when add multi-blocks extent support for insertion interface. Signed-off-by: Fan li <fanofcode.li@samsung.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Diffstat (limited to 'fs/f2fs/extent_cache.c')
-rw-r--r--fs/f2fs/extent_cache.c139
1 files changed, 132 insertions, 7 deletions
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index 362df8cd54d4..32fae8ad5b7e 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -302,6 +302,126 @@ out:
return ret;
}
+
+/*
+ * lookup extent at @fofs, if hit, return the extent
+ * if not, return NULL and
+ * @prev_ex: extent before fofs
+ * @next_ex: extent after fofs
+ * @insert_p: insert point for new extent at fofs
+ * in order to simpfy the insertion after.
+ * tree must stay unchanged between lookup and insertion.
+ */
+static struct extent_node *__lookup_extent_tree_ret(struct extent_tree *et,
+ unsigned int fofs, struct extent_node **prev_ex,
+ struct extent_node **next_ex,
+ struct rb_node ***insert_p,
+ struct rb_node **insert_parent)
+{
+ struct rb_node **pnode = &et->root.rb_node;
+ struct rb_node *parent = NULL, *tmp_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 (*pnode) {
+ parent = *pnode;
+ en = rb_entry(*pnode, struct extent_node, rb_node);
+
+ if (fofs < en->ei.fofs)
+ pnode = &(*pnode)->rb_left;
+ else if (fofs >= en->ei.fofs + en->ei.len)
+ pnode = &(*pnode)->rb_right;
+ else
+ return en;
+ }
+
+ *insert_p = pnode;
+ *insert_parent = parent;
+
+ en = rb_entry(parent, struct extent_node, rb_node);
+ tmp_node = parent;
+ if (parent && fofs > en->ei.fofs)
+ tmp_node = rb_next(parent);
+ *next_ex = tmp_node ?
+ rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
+
+ tmp_node = parent;
+ if (parent && fofs < en->ei.fofs)
+ tmp_node = rb_prev(parent);
+ *prev_ex = tmp_node ?
+ rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
+
+ return NULL;
+}
+
+static struct extent_node *__insert_extent_tree_ret(struct f2fs_sb_info *sbi,
+ struct extent_tree *et, struct extent_info *ei,
+ struct extent_node **den,
+ struct extent_node *prev_ex,
+ struct extent_node *next_ex,
+ struct rb_node **insert_p,
+ struct rb_node *insert_parent)
+{
+ struct rb_node **p = &et->root.rb_node;
+ struct rb_node *parent = NULL;
+ struct extent_node *en = NULL;
+ int merged = 0;
+
+ if (prev_ex && __is_back_mergeable(ei, &prev_ex->ei)) {
+ f2fs_bug_on(sbi, !den);
+ merged = 1;
+ prev_ex->ei.len += ei->len;
+ ei = &prev_ex->ei;
+ en = prev_ex;
+ }
+ if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) {
+ f2fs_bug_on(sbi, !den);
+ if (merged++) {
+ __detach_extent_node(sbi, et, prev_ex);
+ *den = prev_ex;
+ }
+ next_ex->ei.fofs = ei->fofs;
+ next_ex->ei.blk = ei->blk;
+ next_ex->ei.len += ei->len;
+ en = next_ex;
+ }
+ if (merged)
+ goto update_out;
+
+ if (insert_p && insert_parent) {
+ parent = insert_parent;
+ p = insert_p;
+ goto do_insert;
+ }
+
+ while (*p) {
+ parent = *p;
+ en = rb_entry(parent, struct extent_node, rb_node);
+
+ if (ei->fofs < en->ei.fofs)
+ p = &(*p)->rb_left;
+ else if (ei->fofs >= en->ei.fofs + en->ei.len)
+ p = &(*p)->rb_right;
+ else
+ f2fs_bug_on(sbi, 1);
+ }
+do_insert:
+ en = __attach_extent_node(sbi, et, ei, parent, p);
+ if (!en)
+ return NULL;
+update_out:
+ if (en->ei.len > et->largest.len)
+ et->largest = en->ei;
+ et->cached_en = en;
+ return en;
+}
+
/* return true, if on-disk extent should be updated */
static bool f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs,
block_t blkaddr)
@@ -309,8 +429,9 @@ static bool f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs,
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct extent_tree *et = F2FS_I(inode)->extent_tree;
struct extent_node *en = NULL, *en1 = NULL, *en2 = NULL, *en3 = NULL;
- struct extent_node *den = NULL;
+ struct extent_node *den = NULL, *prev_ex = NULL, *next_ex = NULL;
struct extent_info ei, dei, prev;
+ struct rb_node **insert_p = NULL, *insert_parent = NULL;
unsigned int endofs;
if (!et)
@@ -332,20 +453,22 @@ static bool f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs,
f2fs_drop_largest_extent(inode, fofs);
/* 1. lookup and remove existing extent info in cache */
- en = __lookup_extent_tree(et, fofs);
+ en = __lookup_extent_tree_ret(et, fofs, &prev_ex, &next_ex,
+ &insert_p, &insert_parent);
if (!en)
goto update_extent;
dei = en->ei;
__detach_extent_node(sbi, et, en);
- /* 2. if extent can be split more, split and insert the left part */
+ /* 2. if extent can be split, try to split it */
if (dei.len > F2FS_MIN_EXTENT_LEN) {
/* insert left part of split extent into cache */
if (fofs - dei.fofs >= F2FS_MIN_EXTENT_LEN) {
set_extent_info(&ei, dei.fofs, dei.blk,
- fofs - dei.fofs);
- en1 = __insert_extent_tree(sbi, et, &ei, NULL);
+ fofs - dei.fofs);
+ en1 = __insert_extent_tree_ret(sbi, et, &ei, NULL,
+ NULL, NULL, NULL, NULL);
}
/* insert right part of split extent into cache */
@@ -353,7 +476,8 @@ static bool f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs,
if (endofs - fofs >= F2FS_MIN_EXTENT_LEN) {
set_extent_info(&ei, fofs + 1,
fofs - dei.fofs + dei.blk + 1, endofs - fofs);
- en2 = __insert_extent_tree(sbi, et, &ei, NULL);
+ en2 = __insert_extent_tree_ret(sbi, et, &ei, NULL,
+ NULL, NULL, NULL, NULL);
}
}
@@ -361,7 +485,8 @@ update_extent:
/* 3. update extent in extent cache */
if (blkaddr) {
set_extent_info(&ei, fofs, blkaddr, 1);
- en3 = __insert_extent_tree(sbi, et, &ei, &den);
+ en3 = __insert_extent_tree_ret(sbi, et, &ei, &den,
+ prev_ex, next_ex, insert_p, insert_parent);
/* give up extent_cache, if split and small updates happen */
if (dei.len >= 1 &&