f2fs: add new interfaces for extent tree
authorFan Li <fanofcode.li@samsung.com>
Wed, 15 Jul 2015 10:05:17 +0000 (18:05 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Wed, 5 Aug 2015 15:08:08 +0000 (08:08 -0700)
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>
fs/f2fs/extent_cache.c

index 362df8c..32fae8a 100644 (file)
@@ -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 &&