[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20150701013324.GF1496@jaegeuk-mac02.mot.com>
Date: Tue, 30 Jun 2015 18:33:24 -0700
From: Jaegeuk Kim <jaegeuk@...nel.org>
To: Chao Yu <chao2.yu@...sung.com>
Cc: Changman Lee <cm224.lee@...sung.com>,
linux-f2fs-devel@...ts.sourceforge.net,
linux-kernel@...r.kernel.org
Subject: Re: [PATCH 2/2] f2fs: move extent cache codes to extent_cache.c
Hi Chao,
Let me consider this patch after -rc1 to settle down the relevant codes in dev
branch.
Thanks,
On Tue, Jun 30, 2015 at 06:44:00PM +0800, Chao Yu wrote:
> This patch moves extent cache related code from data.c into extent_cache.c
> since extent cache is independent feature, and its code is not relate to
> others in data.c, it's better for us to maintain it in separated place.
>
> Certainly there is no functionality change.
>
> Signed-off-by: Chao Yu <chao2.yu@...sung.com>
> ---
> fs/f2fs/Makefile | 2 +-
> fs/f2fs/data.c | 550 -----------------------------------------------
> fs/f2fs/extent_cache.c | 568 +++++++++++++++++++++++++++++++++++++++++++++++++
> fs/f2fs/f2fs.h | 22 +-
> 4 files changed, 583 insertions(+), 559 deletions(-)
> create mode 100644 fs/f2fs/extent_cache.c
>
> diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile
> index 005251b..08e101e 100644
> --- a/fs/f2fs/Makefile
> +++ b/fs/f2fs/Makefile
> @@ -2,7 +2,7 @@ obj-$(CONFIG_F2FS_FS) += f2fs.o
>
> f2fs-y := dir.o file.o inode.o namei.o hash.o super.o inline.o
> f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o
> -f2fs-y += shrinker.o
> +f2fs-y += shrinker.o extent_cache.o
> f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
> f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
> f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
> diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
> index e96916a..b62fcfe 100644
> --- a/fs/f2fs/data.c
> +++ b/fs/f2fs/data.c
> @@ -26,9 +26,6 @@
> #include "trace.h"
> #include <trace/events/f2fs.h>
>
> -static struct kmem_cache *extent_tree_slab;
> -static struct kmem_cache *extent_node_slab;
> -
> static void f2fs_read_end_io(struct bio *bio, int err)
> {
> struct bio_vec *bvec;
> @@ -266,522 +263,6 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index)
> return err;
> }
>
> -static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
> - struct extent_tree *et, struct extent_info *ei,
> - struct rb_node *parent, struct rb_node **p)
> -{
> - struct extent_node *en;
> -
> - en = kmem_cache_alloc(extent_node_slab, GFP_ATOMIC);
> - if (!en)
> - return NULL;
> -
> - en->ei = *ei;
> - INIT_LIST_HEAD(&en->list);
> -
> - en->et = et;
> - rb_link_node(&en->rb_node, parent, p);
> - rb_insert_color(&en->rb_node, &et->root);
> - et->count++;
> - atomic_inc(&sbi->total_ext_node);
> - return en;
> -}
> -
> -static void __detach_extent_node(struct f2fs_sb_info *sbi,
> - struct extent_tree *et, struct extent_node *en)
> -{
> - 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_tree *__grab_extent_tree(struct inode *inode)
> -{
> - struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> - struct extent_tree *et;
> - nid_t ino = inode->i_ino;
> -
> - down_write(&sbi->extent_tree_lock);
> - et = radix_tree_lookup(&sbi->extent_tree_root, ino);
> - if (!et) {
> - et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS);
> - f2fs_radix_tree_insert(&sbi->extent_tree_root, ino, et);
> - 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;
> - sbi->total_ext_tree++;
> - }
> - atomic_inc(&et->refcount);
> - up_write(&sbi->extent_tree_lock);
> -
> - /* never died untill evict_inode */
> - F2FS_I(inode)->extent_tree = et;
> -
> - return et;
> -}
> -
> -static struct extent_node *__lookup_extent_tree(struct extent_tree *et,
> - unsigned int fofs)
> -{
> - 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)
> - node = node->rb_left;
> - else if (fofs >= en->ei.fofs + en->ei.len)
> - node = node->rb_right;
> - else
> - return en;
> - }
> - return NULL;
> -}
> -
> -static struct extent_node *__try_back_merge(struct f2fs_sb_info *sbi,
> - struct extent_tree *et, struct extent_node *en)
> -{
> - struct extent_node *prev;
> - struct rb_node *node;
> -
> - node = rb_prev(&en->rb_node);
> - if (!node)
> - return NULL;
> -
> - prev = rb_entry(node, struct extent_node, rb_node);
> - if (__is_back_mergeable(&en->ei, &prev->ei)) {
> - en->ei.fofs = prev->ei.fofs;
> - en->ei.blk = prev->ei.blk;
> - en->ei.len += prev->ei.len;
> - __detach_extent_node(sbi, et, prev);
> - return prev;
> - }
> - return NULL;
> -}
> -
> -static struct extent_node *__try_front_merge(struct f2fs_sb_info *sbi,
> - struct extent_tree *et, struct extent_node *en)
> -{
> - struct extent_node *next;
> - struct rb_node *node;
> -
> - node = rb_next(&en->rb_node);
> - if (!node)
> - return NULL;
> -
> - next = rb_entry(node, struct extent_node, rb_node);
> - if (__is_front_mergeable(&en->ei, &next->ei)) {
> - en->ei.len += next->ei.len;
> - __detach_extent_node(sbi, et, next);
> - return next;
> - }
> - return NULL;
> -}
> -
> -static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi,
> - struct extent_tree *et, struct extent_info *ei,
> - struct extent_node **den)
> -{
> - struct rb_node **p = &et->root.rb_node;
> - struct rb_node *parent = NULL;
> - struct extent_node *en;
> -
> - while (*p) {
> - parent = *p;
> - en = rb_entry(parent, struct extent_node, rb_node);
> -
> - if (ei->fofs < en->ei.fofs) {
> - if (__is_front_mergeable(ei, &en->ei)) {
> - en->ei.fofs = ei->fofs;
> - en->ei.blk = ei->blk;
> - en->ei.len += ei->len;
> - if (den)
> - *den = __try_back_merge(sbi, et, en);
> - goto update_out;
> - }
> - p = &(*p)->rb_left;
> - } else if (ei->fofs >= en->ei.fofs + en->ei.len) {
> - if (__is_back_mergeable(ei, &en->ei)) {
> - en->ei.len += ei->len;
> - if (den)
> - *den = __try_front_merge(sbi, et, en);
> - goto update_out;
> - }
> - p = &(*p)->rb_right;
> - } else {
> - f2fs_bug_on(sbi, 1);
> - }
> - }
> -
> - en = __attach_extent_node(sbi, et, ei, parent, p);
> -update_out:
> - if (en && en->ei.len > et->largest.len)
> - et->largest = en->ei;
> - return en;
> -}
> -
> -static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
> - struct extent_tree *et)
> -{
> - struct rb_node *node, *next;
> - struct extent_node *en;
> - unsigned int count = et->count;
> -
> - node = rb_first(&et->root);
> - while (node) {
> - next = rb_next(node);
> - en = rb_entry(node, struct extent_node, rb_node);
> -
> - spin_lock(&sbi->extent_lock);
> - if (!list_empty(&en->list))
> - list_del_init(&en->list);
> - spin_unlock(&sbi->extent_lock);
> -
> - __detach_extent_node(sbi, et, en);
> - kmem_cache_free(extent_node_slab, en);
> - node = next;
> - }
> -
> - return count - et->count;
> -}
> -
> -static bool __try_free_extent_tree(struct f2fs_sb_info *sbi, nid_t ino)
> -{
> - struct extent_tree *et;
> -
> - if (!down_write_trylock(&sbi->extent_tree_lock))
> - return false;
> -
> - et = radix_tree_lookup(&sbi->extent_tree_root, ino);
> - if (!et)
> - goto out;
> -
> - if (__can_free_extent_tree(et)) {
> - radix_tree_delete(&sbi->extent_tree_root, ino);
> - kmem_cache_free(extent_tree_slab, et);
> - sbi->total_ext_tree--;
> - up_write(&sbi->extent_tree_lock);
> - return true;
> - }
> -out:
> - up_write(&sbi->extent_tree_lock);
> - return false;
> -}
> -
> -static void __drop_largest_extent(struct inode *inode, pgoff_t fofs)
> -{
> - struct extent_info *largest = &F2FS_I(inode)->extent_tree->largest;
> -
> - if (largest->fofs <= fofs && largest->fofs + largest->len > fofs)
> - largest->len = 0;
> -}
> -
> -void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
> -{
> - struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> - struct extent_tree *et;
> - struct extent_node *en;
> - struct extent_info ei;
> -
> - if (!f2fs_may_extent_tree(inode))
> - return;
> -
> - et = __grab_extent_tree(inode);
> -
> - if (!i_ext || le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN)
> - return;
> -
> - set_extent_info(&ei, le32_to_cpu(i_ext->fofs),
> - le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len));
> -
> - write_lock(&et->lock);
> - if (et->count)
> - goto out;
> -
> - en = __insert_extent_tree(sbi, et, &ei, NULL);
> - if (en) {
> - et->cached_en = en;
> -
> - spin_lock(&sbi->extent_lock);
> - list_add_tail(&en->list, &sbi->extent_list);
> - spin_unlock(&sbi->extent_lock);
> - }
> -out:
> - write_unlock(&et->lock);
> -}
> -
> -static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
> - struct extent_info *ei)
> -{
> - struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> - struct extent_tree *et = F2FS_I(inode)->extent_tree;
> - struct extent_node *en;
> -
> - f2fs_bug_on(sbi, !et);
> -
> - trace_f2fs_lookup_extent_tree_start(inode, pgofs);
> -
> - read_lock(&et->lock);
> - en = __lookup_extent_tree(et, pgofs);
> - if (en) {
> - *ei = en->ei;
> - spin_lock(&sbi->extent_lock);
> - if (!list_empty(&en->list))
> - list_move_tail(&en->list, &sbi->extent_list);
> - et->cached_en = en;
> - spin_unlock(&sbi->extent_lock);
> - stat_inc_read_hit(sbi->sb);
> - }
> - stat_inc_total_hit(sbi->sb);
> - read_unlock(&et->lock);
> -
> - trace_f2fs_lookup_extent_tree_end(inode, pgofs, en);
> - return en ? true : false;
> -}
> -
> -/* return true, if on-disk extent should be updated */
> -static bool f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs,
> - block_t blkaddr)
> -{
> - 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_info ei, dei, prev;
> - unsigned int endofs;
> -
> - if (!et)
> - return false;
> -
> - trace_f2fs_update_extent_tree(inode, fofs, blkaddr);
> -
> - write_lock(&et->lock);
> -
> - if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) {
> - write_unlock(&et->lock);
> - return false;
> - }
> -
> - prev = et->largest;
> - dei.len = 0;
> -
> - /* 1. lookup and remove existing extent info in cache */
> - en = __lookup_extent_tree(et, fofs);
> - if (!en)
> - goto update_extent;
> -
> - dei = en->ei;
> - __detach_extent_node(sbi, et, en);
> -
> - __drop_largest_extent(inode, fofs);
> -
> - /* 2. if extent can be split more, split and insert the left part */
> - if (dei.len > 1) {
> - /* 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);
> - }
> -
> - /* insert right part of split extent into cache */
> - endofs = dei.fofs + dei.len - 1;
> - 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);
> - }
> - }
> -
> -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);
> -
> - /* give up extent_cache, if split and small updates happen */
> - if (dei.len >= 1 &&
> - prev.len < F2FS_MIN_EXTENT_LEN &&
> - et->largest.len < F2FS_MIN_EXTENT_LEN) {
> - et->largest.len = 0;
> - set_inode_flag(F2FS_I(inode), FI_NO_EXTENT);
> - }
> - }
> -
> - /* 4. update in global extent list */
> - spin_lock(&sbi->extent_lock);
> - if (en && !list_empty(&en->list))
> - list_del(&en->list);
> - /*
> - * en1 and en2 split from en, they will become more and more smaller
> - * fragments after splitting several times. So if the length is smaller
> - * than F2FS_MIN_EXTENT_LEN, we will not add them into extent tree.
> - */
> - if (en1)
> - list_add_tail(&en1->list, &sbi->extent_list);
> - if (en2)
> - list_add_tail(&en2->list, &sbi->extent_list);
> - if (en3) {
> - if (list_empty(&en3->list))
> - list_add_tail(&en3->list, &sbi->extent_list);
> - else
> - list_move_tail(&en3->list, &sbi->extent_list);
> - }
> - if (den && !list_empty(&den->list))
> - list_del(&den->list);
> - spin_unlock(&sbi->extent_lock);
> -
> - /* 5. release extent node */
> - if (en)
> - kmem_cache_free(extent_node_slab, en);
> - if (den)
> - kmem_cache_free(extent_node_slab, den);
> -
> - if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
> - __free_extent_tree(sbi, et);
> -
> - write_unlock(&et->lock);
> -
> - return !__is_extent_same(&prev, &et->largest);
> -}
> -
> -unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
> -{
> - struct extent_tree *et;
> - struct extent_node *en;
> - unsigned int node_cnt = 0, tree_cnt = 0;
> -
> - if (!test_opt(sbi, EXTENT_CACHE))
> - return 0;
> -
> - spin_lock(&sbi->extent_lock);
> - for (; nr_shrink > 0; nr_shrink--) {
> - nid_t ino;
> - bool can_free;
> -
> - if (list_empty(&sbi->extent_list))
> - break;
> - en = list_first_entry(&sbi->extent_list, struct extent_node,
> - list);
> - et = en->et;
> -
> - if (!write_trylock(&et->lock)) {
> - /* refresh this extent node's position in extent list */
> - list_move_tail(&en->list, &sbi->extent_list);
> - continue;
> - }
> -
> - list_del(&en->list);
> - spin_unlock(&sbi->extent_lock);
> -
> - __detach_extent_node(sbi, et, en);
> - kmem_cache_free(extent_node_slab, en);
> -
> - ino = et->ino;
> - can_free = __can_free_extent_tree(et);
> - write_unlock(&et->lock);
> -
> - node_cnt++;
> -
> - if (can_free && __try_free_extent_tree(sbi, ino))
> - tree_cnt++;
> -
> - spin_lock(&sbi->extent_lock);
> - }
> - spin_unlock(&sbi->extent_lock);
> -
> - trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt);
> -
> - return node_cnt + tree_cnt;
> -}
> -
> -void f2fs_destroy_extent_node(struct inode *inode)
> -{
> - struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> - struct extent_tree *et = F2FS_I(inode)->extent_tree;
> - unsigned int node_cnt = 0;
> -
> - if (!et)
> - return;
> -
> - write_lock(&et->lock);
> - node_cnt = __free_extent_tree(sbi, et);
> - write_unlock(&et->lock);
> -}
> -
> -void f2fs_destroy_extent_tree(struct inode *inode)
> -{
> - struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> - struct extent_tree *et = F2FS_I(inode)->extent_tree;
> - unsigned int node_cnt = 0;
> -
> - if (!et)
> - return;
> -
> - if (inode->i_nlink && !is_bad_inode(inode) && et->count) {
> - atomic_dec(&et->refcount);
> - return;
> - }
> -
> - /* free all extent info belong to this extent tree */
> - f2fs_destroy_extent_node(inode);
> -
> - /* delete extent tree entry in radix tree */
> - down_write(&sbi->extent_tree_lock);
> - atomic_dec(&et->refcount);
> - f2fs_bug_on(sbi, !__can_free_extent_tree(et));
> - radix_tree_delete(&sbi->extent_tree_root, inode->i_ino);
> - kmem_cache_free(extent_tree_slab, et);
> - sbi->total_ext_tree--;
> - up_write(&sbi->extent_tree_lock);
> -
> - F2FS_I(inode)->extent_tree = NULL;
> -
> - 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 (!f2fs_may_extent_tree(inode))
> - return false;
> -
> - return f2fs_lookup_extent_tree(inode, pgofs, ei);
> -}
> -
> -void f2fs_update_extent_cache(struct dnode_of_data *dn)
> -{
> - struct f2fs_inode_info *fi = F2FS_I(dn->inode);
> - pgoff_t fofs;
> -
> - if (!f2fs_may_extent_tree(dn->inode))
> - return;
> -
> - f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR);
> -
> - fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
> - dn->ofs_in_node;
> -
> - if (f2fs_update_extent_tree(dn->inode, fofs, dn->data_blkaddr))
> - sync_inode_page(dn);
> -}
> -
> struct page *get_read_data_page(struct inode *inode, pgoff_t index, int rw)
> {
> struct address_space *mapping = inode->i_mapping;
> @@ -1971,37 +1452,6 @@ static sector_t f2fs_bmap(struct address_space *mapping, sector_t block)
> return generic_block_bmap(mapping, block, get_data_block);
> }
>
> -void init_extent_cache_info(struct f2fs_sb_info *sbi)
> -{
> - INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO);
> - init_rwsem(&sbi->extent_tree_lock);
> - INIT_LIST_HEAD(&sbi->extent_list);
> - spin_lock_init(&sbi->extent_lock);
> - sbi->total_ext_tree = 0;
> - atomic_set(&sbi->total_ext_node, 0);
> -}
> -
> -int __init create_extent_cache(void)
> -{
> - extent_tree_slab = f2fs_kmem_cache_create("f2fs_extent_tree",
> - sizeof(struct extent_tree));
> - if (!extent_tree_slab)
> - return -ENOMEM;
> - extent_node_slab = f2fs_kmem_cache_create("f2fs_extent_node",
> - sizeof(struct extent_node));
> - if (!extent_node_slab) {
> - kmem_cache_destroy(extent_tree_slab);
> - return -ENOMEM;
> - }
> - return 0;
> -}
> -
> -void destroy_extent_cache(void)
> -{
> - kmem_cache_destroy(extent_node_slab);
> - kmem_cache_destroy(extent_tree_slab);
> -}
> -
> const struct address_space_operations f2fs_dblock_aops = {
> .readpage = f2fs_read_data_page,
> .readpages = f2fs_read_data_pages,
> diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
> new file mode 100644
> index 0000000..25be61e
> --- /dev/null
> +++ b/fs/f2fs/extent_cache.c
> @@ -0,0 +1,568 @@
> +/*
> + * f2fs extent cache support
> + *
> + * Copyright (c) 2015 Motorola Mobility
> + * Copyright (c) 2015 Samsung Electronics
> + * Authors: Jaegeuk Kim <jaegeuk@...nel.org>
> + * Chao Yu <chao2.yu@...sung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/f2fs_fs.h>
> +
> +#include "f2fs.h"
> +#include "node.h"
> +#include <trace/events/f2fs.h>
> +
> +static struct kmem_cache *extent_tree_slab;
> +static struct kmem_cache *extent_node_slab;
> +
> +static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
> + struct extent_tree *et, struct extent_info *ei,
> + struct rb_node *parent, struct rb_node **p)
> +{
> + struct extent_node *en;
> +
> + en = kmem_cache_alloc(extent_node_slab, GFP_ATOMIC);
> + if (!en)
> + return NULL;
> +
> + en->ei = *ei;
> + INIT_LIST_HEAD(&en->list);
> +
> + en->et = et;
> + rb_link_node(&en->rb_node, parent, p);
> + rb_insert_color(&en->rb_node, &et->root);
> + et->count++;
> + atomic_inc(&sbi->total_ext_node);
> + return en;
> +}
> +
> +static void __detach_extent_node(struct f2fs_sb_info *sbi,
> + struct extent_tree *et, struct extent_node *en)
> +{
> + 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_tree *__grab_extent_tree(struct inode *inode)
> +{
> + struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> + struct extent_tree *et;
> + nid_t ino = inode->i_ino;
> +
> + down_write(&sbi->extent_tree_lock);
> + et = radix_tree_lookup(&sbi->extent_tree_root, ino);
> + if (!et) {
> + et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS);
> + f2fs_radix_tree_insert(&sbi->extent_tree_root, ino, et);
> + 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;
> + sbi->total_ext_tree++;
> + }
> + atomic_inc(&et->refcount);
> + up_write(&sbi->extent_tree_lock);
> +
> + /* never died until evict_inode */
> + F2FS_I(inode)->extent_tree = et;
> +
> + return et;
> +}
> +
> +static struct extent_node *__lookup_extent_tree(struct extent_tree *et,
> + unsigned int fofs)
> +{
> + 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)
> + node = node->rb_left;
> + else if (fofs >= en->ei.fofs + en->ei.len)
> + node = node->rb_right;
> + else
> + return en;
> + }
> + return NULL;
> +}
> +
> +static struct extent_node *__try_back_merge(struct f2fs_sb_info *sbi,
> + struct extent_tree *et, struct extent_node *en)
> +{
> + struct extent_node *prev;
> + struct rb_node *node;
> +
> + node = rb_prev(&en->rb_node);
> + if (!node)
> + return NULL;
> +
> + prev = rb_entry(node, struct extent_node, rb_node);
> + if (__is_back_mergeable(&en->ei, &prev->ei)) {
> + en->ei.fofs = prev->ei.fofs;
> + en->ei.blk = prev->ei.blk;
> + en->ei.len += prev->ei.len;
> + __detach_extent_node(sbi, et, prev);
> + return prev;
> + }
> + return NULL;
> +}
> +
> +static struct extent_node *__try_front_merge(struct f2fs_sb_info *sbi,
> + struct extent_tree *et, struct extent_node *en)
> +{
> + struct extent_node *next;
> + struct rb_node *node;
> +
> + node = rb_next(&en->rb_node);
> + if (!node)
> + return NULL;
> +
> + next = rb_entry(node, struct extent_node, rb_node);
> + if (__is_front_mergeable(&en->ei, &next->ei)) {
> + en->ei.len += next->ei.len;
> + __detach_extent_node(sbi, et, next);
> + return next;
> + }
> + return NULL;
> +}
> +
> +static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi,
> + struct extent_tree *et, struct extent_info *ei,
> + struct extent_node **den)
> +{
> + struct rb_node **p = &et->root.rb_node;
> + struct rb_node *parent = NULL;
> + struct extent_node *en;
> +
> + while (*p) {
> + parent = *p;
> + en = rb_entry(parent, struct extent_node, rb_node);
> +
> + if (ei->fofs < en->ei.fofs) {
> + if (__is_front_mergeable(ei, &en->ei)) {
> + en->ei.fofs = ei->fofs;
> + en->ei.blk = ei->blk;
> + en->ei.len += ei->len;
> + if (den)
> + *den = __try_back_merge(sbi, et, en);
> + goto update_out;
> + }
> + p = &(*p)->rb_left;
> + } else if (ei->fofs >= en->ei.fofs + en->ei.len) {
> + if (__is_back_mergeable(ei, &en->ei)) {
> + en->ei.len += ei->len;
> + if (den)
> + *den = __try_front_merge(sbi, et, en);
> + goto update_out;
> + }
> + p = &(*p)->rb_right;
> + } else {
> + f2fs_bug_on(sbi, 1);
> + }
> + }
> +
> + en = __attach_extent_node(sbi, et, ei, parent, p);
> +update_out:
> + if (en && en->ei.len > et->largest.len)
> + et->largest = en->ei;
> + return en;
> +}
> +
> +static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
> + struct extent_tree *et)
> +{
> + struct rb_node *node, *next;
> + struct extent_node *en;
> + unsigned int count = et->count;
> +
> + node = rb_first(&et->root);
> + while (node) {
> + next = rb_next(node);
> + en = rb_entry(node, struct extent_node, rb_node);
> +
> + spin_lock(&sbi->extent_lock);
> + if (!list_empty(&en->list))
> + list_del_init(&en->list);
> + spin_unlock(&sbi->extent_lock);
> +
> + __detach_extent_node(sbi, et, en);
> + kmem_cache_free(extent_node_slab, en);
> + node = next;
> + }
> +
> + return count - et->count;
> +}
> +
> +static bool __try_free_extent_tree(struct f2fs_sb_info *sbi, nid_t ino)
> +{
> + struct extent_tree *et;
> +
> + if (!down_write_trylock(&sbi->extent_tree_lock))
> + return false;
> +
> + et = radix_tree_lookup(&sbi->extent_tree_root, ino);
> + if (!et)
> + goto out;
> +
> + if (__can_free_extent_tree(et)) {
> + radix_tree_delete(&sbi->extent_tree_root, ino);
> + kmem_cache_free(extent_tree_slab, et);
> + sbi->total_ext_tree--;
> + up_write(&sbi->extent_tree_lock);
> + return true;
> + }
> +out:
> + up_write(&sbi->extent_tree_lock);
> + return false;
> +}
> +
> +void __drop_largest_extent(struct inode *inode, pgoff_t fofs)
> +{
> + struct extent_info *largest = &F2FS_I(inode)->extent_tree->largest;
> +
> + if (largest->fofs <= fofs && largest->fofs + largest->len > fofs)
> + largest->len = 0;
> +}
> +
> +void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
> +{
> + struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> + struct extent_tree *et;
> + struct extent_node *en;
> + struct extent_info ei;
> +
> + if (!f2fs_may_extent_tree(inode))
> + return;
> +
> + et = __grab_extent_tree(inode);
> +
> + if (!i_ext || le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN)
> + return;
> +
> + set_extent_info(&ei, le32_to_cpu(i_ext->fofs),
> + le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len));
> +
> + write_lock(&et->lock);
> + if (et->count)
> + goto out;
> +
> + en = __insert_extent_tree(sbi, et, &ei, NULL);
> + if (en) {
> + et->cached_en = en;
> +
> + spin_lock(&sbi->extent_lock);
> + list_add_tail(&en->list, &sbi->extent_list);
> + spin_unlock(&sbi->extent_lock);
> + }
> +out:
> + write_unlock(&et->lock);
> +}
> +
> +static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
> + struct extent_info *ei)
> +{
> + struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> + struct extent_tree *et = F2FS_I(inode)->extent_tree;
> + struct extent_node *en;
> +
> + f2fs_bug_on(sbi, !et);
> +
> + trace_f2fs_lookup_extent_tree_start(inode, pgofs);
> +
> + read_lock(&et->lock);
> + en = __lookup_extent_tree(et, pgofs);
> + if (en) {
> + *ei = en->ei;
> + spin_lock(&sbi->extent_lock);
> + if (!list_empty(&en->list))
> + list_move_tail(&en->list, &sbi->extent_list);
> + et->cached_en = en;
> + spin_unlock(&sbi->extent_lock);
> + stat_inc_read_hit(sbi->sb);
> + }
> + stat_inc_total_hit(sbi->sb);
> + read_unlock(&et->lock);
> +
> + trace_f2fs_lookup_extent_tree_end(inode, pgofs, en);
> + return en ? true : false;
> +}
> +
> +/* return true, if on-disk extent should be updated */
> +static bool f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs,
> + block_t blkaddr)
> +{
> + 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_info ei, dei, prev;
> + unsigned int endofs;
> +
> + if (!et)
> + return false;
> +
> + trace_f2fs_update_extent_tree(inode, fofs, blkaddr);
> +
> + write_lock(&et->lock);
> +
> + if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) {
> + write_unlock(&et->lock);
> + return false;
> + }
> +
> + prev = et->largest;
> + dei.len = 0;
> +
> + /* 1. lookup and remove existing extent info in cache */
> + en = __lookup_extent_tree(et, fofs);
> + if (!en)
> + goto update_extent;
> +
> + dei = en->ei;
> + __detach_extent_node(sbi, et, en);
> +
> + __drop_largest_extent(inode, fofs);
> +
> + /* 2. if extent can be split more, split and insert the left part */
> + if (dei.len > 1) {
> + /* 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);
> + }
> +
> + /* insert right part of split extent into cache */
> + endofs = dei.fofs + dei.len - 1;
> + 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);
> + }
> + }
> +
> +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);
> +
> + /* give up extent_cache, if split and small updates happen */
> + if (dei.len >= 1 &&
> + prev.len < F2FS_MIN_EXTENT_LEN &&
> + et->largest.len < F2FS_MIN_EXTENT_LEN) {
> + et->largest.len = 0;
> + set_inode_flag(F2FS_I(inode), FI_NO_EXTENT);
> + }
> + }
> +
> + /* 4. update in global extent list */
> + spin_lock(&sbi->extent_lock);
> + if (en && !list_empty(&en->list))
> + list_del(&en->list);
> + /*
> + * en1 and en2 split from en, they will become more and more smaller
> + * fragments after splitting several times. So if the length is smaller
> + * than F2FS_MIN_EXTENT_LEN, we will not add them into extent tree.
> + */
> + if (en1)
> + list_add_tail(&en1->list, &sbi->extent_list);
> + if (en2)
> + list_add_tail(&en2->list, &sbi->extent_list);
> + if (en3) {
> + if (list_empty(&en3->list))
> + list_add_tail(&en3->list, &sbi->extent_list);
> + else
> + list_move_tail(&en3->list, &sbi->extent_list);
> + }
> + if (den && !list_empty(&den->list))
> + list_del(&den->list);
> + spin_unlock(&sbi->extent_lock);
> +
> + /* 5. release extent node */
> + if (en)
> + kmem_cache_free(extent_node_slab, en);
> + if (den)
> + kmem_cache_free(extent_node_slab, den);
> +
> + if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
> + __free_extent_tree(sbi, et);
> +
> + write_unlock(&et->lock);
> +
> + return !__is_extent_same(&prev, &et->largest);
> +}
> +
> +unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
> +{
> + struct extent_tree *et;
> + struct extent_node *en;
> + unsigned int node_cnt = 0, tree_cnt = 0;
> +
> + if (!test_opt(sbi, EXTENT_CACHE))
> + return 0;
> +
> + spin_lock(&sbi->extent_lock);
> + for (; nr_shrink > 0; nr_shrink--) {
> + nid_t ino;
> + bool can_free;
> +
> + if (list_empty(&sbi->extent_list))
> + break;
> + en = list_first_entry(&sbi->extent_list, struct extent_node,
> + list);
> + et = en->et;
> +
> + if (!write_trylock(&et->lock)) {
> + /* refresh this extent node's position in extent list */
> + list_move_tail(&en->list, &sbi->extent_list);
> + continue;
> + }
> +
> + list_del(&en->list);
> + spin_unlock(&sbi->extent_lock);
> +
> + __detach_extent_node(sbi, et, en);
> + kmem_cache_free(extent_node_slab, en);
> +
> + ino = et->ino;
> + can_free = __can_free_extent_tree(et);
> + write_unlock(&et->lock);
> +
> + node_cnt++;
> +
> + if (can_free && __try_free_extent_tree(sbi, ino))
> + tree_cnt++;
> +
> + spin_lock(&sbi->extent_lock);
> + }
> + spin_unlock(&sbi->extent_lock);
> +
> + trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt);
> +
> + return node_cnt + tree_cnt;
> +}
> +
> +void f2fs_destroy_extent_node(struct inode *inode)
> +{
> + struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> + struct extent_tree *et = F2FS_I(inode)->extent_tree;
> + unsigned int node_cnt = 0;
> +
> + if (!et)
> + return;
> +
> + write_lock(&et->lock);
> + node_cnt = __free_extent_tree(sbi, et);
> + write_unlock(&et->lock);
> +}
> +
> +void f2fs_destroy_extent_tree(struct inode *inode)
> +{
> + struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> + struct extent_tree *et = F2FS_I(inode)->extent_tree;
> + unsigned int node_cnt = 0;
> +
> + if (!et)
> + return;
> +
> + if (inode->i_nlink && !is_bad_inode(inode) && et->count) {
> + atomic_dec(&et->refcount);
> + return;
> + }
> +
> + /* free all extent info belong to this extent tree */
> + f2fs_destroy_extent_node(inode);
> +
> + /* delete extent tree entry in radix tree */
> + down_write(&sbi->extent_tree_lock);
> + atomic_dec(&et->refcount);
> + f2fs_bug_on(sbi, !__can_free_extent_tree(et));
> + radix_tree_delete(&sbi->extent_tree_root, inode->i_ino);
> + kmem_cache_free(extent_tree_slab, et);
> + sbi->total_ext_tree--;
> + up_write(&sbi->extent_tree_lock);
> +
> + F2FS_I(inode)->extent_tree = NULL;
> +
> + trace_f2fs_destroy_extent_tree(inode, node_cnt);
> +}
> +
> +bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs,
> + struct extent_info *ei)
> +{
> + if (!f2fs_may_extent_tree(inode))
> + return false;
> +
> + return f2fs_lookup_extent_tree(inode, pgofs, ei);
> +}
> +
> +void f2fs_update_extent_cache(struct dnode_of_data *dn)
> +{
> + struct f2fs_inode_info *fi = F2FS_I(dn->inode);
> + pgoff_t fofs;
> +
> + if (!f2fs_may_extent_tree(dn->inode))
> + return;
> +
> + f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR);
> +
> + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
> + dn->ofs_in_node;
> +
> + if (f2fs_update_extent_tree(dn->inode, fofs, dn->data_blkaddr))
> + sync_inode_page(dn);
> +}
> +
> +void init_extent_cache_info(struct f2fs_sb_info *sbi)
> +{
> + INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO);
> + init_rwsem(&sbi->extent_tree_lock);
> + INIT_LIST_HEAD(&sbi->extent_list);
> + spin_lock_init(&sbi->extent_lock);
> + sbi->total_ext_tree = 0;
> + atomic_set(&sbi->total_ext_node, 0);
> +}
> +
> +int __init create_extent_cache(void)
> +{
> + extent_tree_slab = f2fs_kmem_cache_create("f2fs_extent_tree",
> + sizeof(struct extent_tree));
> + if (!extent_tree_slab)
> + return -ENOMEM;
> + extent_node_slab = f2fs_kmem_cache_create("f2fs_extent_node",
> + sizeof(struct extent_node));
> + if (!extent_node_slab) {
> + kmem_cache_destroy(extent_tree_slab);
> + return -ENOMEM;
> + }
> + return 0;
> +}
> +
> +void destroy_extent_cache(void)
> +{
> + kmem_cache_destroy(extent_node_slab);
> + kmem_cache_destroy(extent_tree_slab);
> +}
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index ee4c04a..7b22595 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -1771,20 +1771,12 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *);
> void set_data_blkaddr(struct dnode_of_data *);
> int reserve_new_block(struct dnode_of_data *);
> int f2fs_reserve_block(struct dnode_of_data *, pgoff_t);
> -unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int);
> -void f2fs_init_extent_tree(struct inode *, struct f2fs_extent *);
> -void f2fs_destroy_extent_node(struct inode *);
> -void f2fs_destroy_extent_tree(struct inode *);
> -void f2fs_update_extent_cache(struct dnode_of_data *);
> struct page *get_read_data_page(struct inode *, pgoff_t, int);
> struct page *find_data_page(struct inode *, pgoff_t);
> struct page *get_lock_data_page(struct inode *, pgoff_t);
> struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool);
> int do_write_data_page(struct f2fs_io_info *);
> int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64);
> -void init_extent_cache_info(struct f2fs_sb_info *);
> -int __init create_extent_cache(void);
> -void destroy_extent_cache(void);
> void f2fs_invalidate_page(struct page *, unsigned int, unsigned int);
> int f2fs_release_page(struct page *, gfp_t);
>
> @@ -1982,6 +1974,20 @@ void f2fs_join_shrinker(struct f2fs_sb_info *);
> void f2fs_leave_shrinker(struct f2fs_sb_info *);
>
> /*
> + * extent_cache.c
> + */
> +unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int);
> +void __drop_largest_extent(struct inode *, pgoff_t);
> +void f2fs_init_extent_tree(struct inode *, struct f2fs_extent *);
> +void f2fs_destroy_extent_node(struct inode *);
> +void f2fs_destroy_extent_tree(struct inode *);
> +bool f2fs_lookup_extent_cache(struct inode *, pgoff_t, struct extent_info *);
> +void f2fs_update_extent_cache(struct dnode_of_data *);
> +void init_extent_cache_info(struct f2fs_sb_info *);
> +int __init create_extent_cache(void);
> +void destroy_extent_cache(void);
> +
> +/*
> * crypto support
> */
> static inline int f2fs_encrypted_inode(struct inode *inode)
> --
> 2.4.2
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists