Merge tag 'for-f2fs-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs

Pull f2fs updates from Jaegeuk Kim:
 "This patch-set includes lots of bug fixes based on clean-ups and
  refactored codes.  And inline_dir was introduced and two minor mount
  options were added.  Details from signed tag:

  This series includes the following enhancement with refactored flows.
   - fix inmemory page operations
   - fix wrong inline_data & inline_dir logics
   - enhance memory and IO control under memory pressure
   - consider preemption on radix_tree operation
   - fix memory leaks and deadlocks

  But also, there are a couple of new features:
   - support inline_dir to store dentries inside inode page
   - add -o fastboot to reduce booting time
   - implement -o dirsync

  And a lot of clean-ups and minor bug fixes as well"

* tag 'for-f2fs-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (88 commits)
  f2fs: avoid to ra unneeded blocks in recover flow
  f2fs: introduce is_valid_blkaddr to cleanup codes in ra_meta_pages
  f2fs: fix to enable readahead for SSA/CP blocks
  f2fs: use atomic for counting inode with inline_{dir,inode} flag
  f2fs: cleanup path to need cp at fsync
  f2fs: check if inode state is dirty at fsync
  f2fs: count the number of inmemory pages
  f2fs: release inmemory pages when the file was closed
  f2fs: set page private for inmemory pages for truncation
  f2fs: count inline_xx in do_read_inode
  f2fs: do retry operations with cond_resched
  f2fs: call radix_tree_preload before radix_tree_insert
  f2fs: use rw_semaphore for nat entry lock
  f2fs: fix missing kmem_cache_free
  f2fs: more fast lookup for gc_inode list
  f2fs: cleanup redundant macro
  f2fs: fix to return correct error number in f2fs_write_begin
  f2fs: cleanup if-statement of phase in gc_data_segment
  f2fs: fix to recover converted inline_data
  f2fs: make clean the page before writing
  ...
This commit is contained in:
Linus Torvalds 2014-12-10 15:41:28 -08:00
commit 4b0a268eec
23 changed files with 1592 additions and 720 deletions

View File

@ -122,6 +122,10 @@ disable_ext_identify Disable the extension list configured by mkfs, so f2fs
inline_xattr Enable the inline xattrs feature. inline_xattr Enable the inline xattrs feature.
inline_data Enable the inline data feature: New created small(<~3.4k) inline_data Enable the inline data feature: New created small(<~3.4k)
files can be written into inode block. files can be written into inode block.
inline_dentry Enable the inline dir feature: data in new created
directory entries can be written into inode block. The
space of inode block which is used to store inline
dentries is limited to ~3.4k.
flush_merge Merge concurrent cache_flush commands as much as possible flush_merge Merge concurrent cache_flush commands as much as possible
to eliminate redundant command issues. If the underlying to eliminate redundant command issues. If the underlying
device handles the cache_flush command relatively slowly, device handles the cache_flush command relatively slowly,
@ -131,6 +135,9 @@ nobarrier This option can be used if underlying storage guarantees
If this option is set, no cache_flush commands are issued If this option is set, no cache_flush commands are issued
but f2fs still guarantees the write ordering of all the but f2fs still guarantees the write ordering of all the
data writes. data writes.
fastboot This option is used when a system wants to reduce mount
time as much as possible, even though normal performance
can be sacrificed.
================================================================================ ================================================================================
DEBUGFS ENTRIES DEBUGFS ENTRIES

View File

@ -162,7 +162,8 @@ fail:
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
struct posix_acl *f2fs_get_acl(struct inode *inode, int type) static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
struct page *dpage)
{ {
int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
void *value = NULL; void *value = NULL;
@ -172,12 +173,13 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
if (type == ACL_TYPE_ACCESS) if (type == ACL_TYPE_ACCESS)
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
retval = f2fs_getxattr(inode, name_index, "", NULL, 0); retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
if (retval > 0) { if (retval > 0) {
value = kmalloc(retval, GFP_F2FS_ZERO); value = kmalloc(retval, GFP_F2FS_ZERO);
if (!value) if (!value)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
retval = f2fs_getxattr(inode, name_index, "", value, retval); retval = f2fs_getxattr(inode, name_index, "", value,
retval, dpage);
} }
if (retval > 0) if (retval > 0)
@ -194,6 +196,11 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
return acl; return acl;
} }
struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
{
return __f2fs_get_acl(inode, type, NULL);
}
static int __f2fs_set_acl(struct inode *inode, int type, static int __f2fs_set_acl(struct inode *inode, int type,
struct posix_acl *acl, struct page *ipage) struct posix_acl *acl, struct page *ipage)
{ {
@ -229,7 +236,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
if (acl) { if (acl) {
value = f2fs_acl_to_disk(acl, &size); value = f2fs_acl_to_disk(acl, &size);
if (IS_ERR(value)) { if (IS_ERR(value)) {
cond_clear_inode_flag(fi, FI_ACL_MODE); clear_inode_flag(fi, FI_ACL_MODE);
return (int)PTR_ERR(value); return (int)PTR_ERR(value);
} }
} }
@ -240,7 +247,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
if (!error) if (!error)
set_cached_acl(inode, type, acl); set_cached_acl(inode, type, acl);
cond_clear_inode_flag(fi, FI_ACL_MODE); clear_inode_flag(fi, FI_ACL_MODE);
return error; return error;
} }
@ -249,12 +256,137 @@ int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
return __f2fs_set_acl(inode, type, acl, NULL); return __f2fs_set_acl(inode, type, acl, NULL);
} }
int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage) /*
* Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
* are copied from posix_acl.c
*/
static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
gfp_t flags)
{ {
struct posix_acl *default_acl, *acl; struct posix_acl *clone = NULL;
if (acl) {
int size = sizeof(struct posix_acl) + acl->a_count *
sizeof(struct posix_acl_entry);
clone = kmemdup(acl, size, flags);
if (clone)
atomic_set(&clone->a_refcount, 1);
}
return clone;
}
static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
{
struct posix_acl_entry *pa, *pe;
struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
umode_t mode = *mode_p;
int not_equiv = 0;
/* assert(atomic_read(acl->a_refcount) == 1); */
FOREACH_ACL_ENTRY(pa, acl, pe) {
switch(pa->e_tag) {
case ACL_USER_OBJ:
pa->e_perm &= (mode >> 6) | ~S_IRWXO;
mode &= (pa->e_perm << 6) | ~S_IRWXU;
break;
case ACL_USER:
case ACL_GROUP:
not_equiv = 1;
break;
case ACL_GROUP_OBJ:
group_obj = pa;
break;
case ACL_OTHER:
pa->e_perm &= mode | ~S_IRWXO;
mode &= pa->e_perm | ~S_IRWXO;
break;
case ACL_MASK:
mask_obj = pa;
not_equiv = 1;
break;
default:
return -EIO;
}
}
if (mask_obj) {
mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
} else {
if (!group_obj)
return -EIO;
group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
}
*mode_p = (*mode_p & ~S_IRWXUGO) | mode;
return not_equiv;
}
static int f2fs_acl_create(struct inode *dir, umode_t *mode,
struct posix_acl **default_acl, struct posix_acl **acl,
struct page *dpage)
{
struct posix_acl *p;
int ret;
if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
goto no_acl;
p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
if (IS_ERR(p)) {
if (p == ERR_PTR(-EOPNOTSUPP))
goto apply_umask;
return PTR_ERR(p);
}
if (!p)
goto apply_umask;
*acl = f2fs_acl_clone(p, GFP_NOFS);
if (!*acl)
return -ENOMEM;
ret = f2fs_acl_create_masq(*acl, mode);
if (ret < 0) {
posix_acl_release(*acl);
return -ENOMEM;
}
if (ret == 0) {
posix_acl_release(*acl);
*acl = NULL;
}
if (!S_ISDIR(*mode)) {
posix_acl_release(p);
*default_acl = NULL;
} else {
*default_acl = p;
}
return 0;
apply_umask:
*mode &= ~current_umask();
no_acl:
*default_acl = NULL;
*acl = NULL;
return 0;
}
int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
struct page *dpage)
{
struct posix_acl *default_acl = NULL, *acl = NULL;
int error = 0; int error = 0;
error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
if (error) if (error)
return error; return error;

View File

@ -38,14 +38,15 @@ struct f2fs_acl_header {
extern struct posix_acl *f2fs_get_acl(struct inode *, int); extern struct posix_acl *f2fs_get_acl(struct inode *, int);
extern int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type); extern int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
extern int f2fs_init_acl(struct inode *, struct inode *, struct page *); extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
struct page *);
#else #else
#define f2fs_check_acl NULL #define f2fs_check_acl NULL
#define f2fs_get_acl NULL #define f2fs_get_acl NULL
#define f2fs_set_acl NULL #define f2fs_set_acl NULL
static inline int f2fs_init_acl(struct inode *inode, struct inode *dir, static inline int f2fs_init_acl(struct inode *inode, struct inode *dir,
struct page *page) struct page *ipage, struct page *dpage)
{ {
return 0; return 0;
} }

View File

@ -72,36 +72,36 @@ out:
return page; return page;
} }
struct page *get_meta_page_ra(struct f2fs_sb_info *sbi, pgoff_t index) static inline bool is_valid_blkaddr(struct f2fs_sb_info *sbi,
{ block_t blkaddr, int type)
bool readahead = false;
struct page *page;
page = find_get_page(META_MAPPING(sbi), index);
if (!page || (page && !PageUptodate(page)))
readahead = true;
f2fs_put_page(page, 0);
if (readahead)
ra_meta_pages(sbi, index, MAX_BIO_BLOCKS(sbi), META_POR);
return get_meta_page(sbi, index);
}
static inline block_t get_max_meta_blks(struct f2fs_sb_info *sbi, int type)
{ {
switch (type) { switch (type) {
case META_NAT: case META_NAT:
return NM_I(sbi)->max_nid / NAT_ENTRY_PER_BLOCK; break;
case META_SIT: case META_SIT:
return SIT_BLK_CNT(sbi); if (unlikely(blkaddr >= SIT_BLK_CNT(sbi)))
return false;
break;
case META_SSA: case META_SSA:
if (unlikely(blkaddr >= MAIN_BLKADDR(sbi) ||
blkaddr < SM_I(sbi)->ssa_blkaddr))
return false;
break;
case META_CP: case META_CP:
return 0; if (unlikely(blkaddr >= SIT_I(sbi)->sit_base_addr ||
blkaddr < __start_cp_addr(sbi)))
return false;
break;
case META_POR: case META_POR:
return MAX_BLKADDR(sbi); if (unlikely(blkaddr >= MAX_BLKADDR(sbi) ||
blkaddr < MAIN_BLKADDR(sbi)))
return false;
break;
default: default:
BUG(); BUG();
} }
return true;
} }
/* /*
@ -112,7 +112,6 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type
block_t prev_blk_addr = 0; block_t prev_blk_addr = 0;
struct page *page; struct page *page;
block_t blkno = start; block_t blkno = start;
block_t max_blks = get_max_meta_blks(sbi, type);
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
.type = META, .type = META,
@ -122,18 +121,20 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type
for (; nrpages-- > 0; blkno++) { for (; nrpages-- > 0; blkno++) {
block_t blk_addr; block_t blk_addr;
if (!is_valid_blkaddr(sbi, blkno, type))
goto out;
switch (type) { switch (type) {
case META_NAT: case META_NAT:
/* get nat block addr */ if (unlikely(blkno >=
if (unlikely(blkno >= max_blks)) NAT_BLOCK_OFFSET(NM_I(sbi)->max_nid)))
blkno = 0; blkno = 0;
/* get nat block addr */
blk_addr = current_nat_addr(sbi, blk_addr = current_nat_addr(sbi,
blkno * NAT_ENTRY_PER_BLOCK); blkno * NAT_ENTRY_PER_BLOCK);
break; break;
case META_SIT: case META_SIT:
/* get sit block addr */ /* get sit block addr */
if (unlikely(blkno >= max_blks))
goto out;
blk_addr = current_sit_addr(sbi, blk_addr = current_sit_addr(sbi,
blkno * SIT_ENTRY_PER_BLOCK); blkno * SIT_ENTRY_PER_BLOCK);
if (blkno != start && prev_blk_addr + 1 != blk_addr) if (blkno != start && prev_blk_addr + 1 != blk_addr)
@ -143,10 +144,6 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type
case META_SSA: case META_SSA:
case META_CP: case META_CP:
case META_POR: case META_POR:
if (unlikely(blkno >= max_blks))
goto out;
if (unlikely(blkno < SEG0_BLKADDR(sbi)))
goto out;
blk_addr = blkno; blk_addr = blkno;
break; break;
default: default:
@ -169,6 +166,20 @@ out:
return blkno - start; return blkno - start;
} }
void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index)
{
struct page *page;
bool readahead = false;
page = find_get_page(META_MAPPING(sbi), index);
if (!page || (page && !PageUptodate(page)))
readahead = true;
f2fs_put_page(page, 0);
if (readahead)
ra_meta_pages(sbi, index, MAX_BIO_BLOCKS(sbi), META_POR);
}
static int f2fs_write_meta_page(struct page *page, static int f2fs_write_meta_page(struct page *page,
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
@ -178,7 +189,7 @@ static int f2fs_write_meta_page(struct page *page,
if (unlikely(sbi->por_doing)) if (unlikely(sbi->por_doing))
goto redirty_out; goto redirty_out;
if (wbc->for_reclaim) if (wbc->for_reclaim && page->index < GET_SUM_BLOCK(sbi, 0))
goto redirty_out; goto redirty_out;
if (unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
goto redirty_out; goto redirty_out;
@ -187,6 +198,9 @@ static int f2fs_write_meta_page(struct page *page,
write_meta_page(sbi, page); write_meta_page(sbi, page);
dec_page_count(sbi, F2FS_DIRTY_META); dec_page_count(sbi, F2FS_DIRTY_META);
unlock_page(page); unlock_page(page);
if (wbc->for_reclaim)
f2fs_submit_merged_bio(sbi, META, WRITE);
return 0; return 0;
redirty_out: redirty_out:
@ -298,46 +312,57 @@ const struct address_space_operations f2fs_meta_aops = {
static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
{ {
struct inode_management *im = &sbi->im[type];
struct ino_entry *e; struct ino_entry *e;
retry: retry:
spin_lock(&sbi->ino_lock[type]); if (radix_tree_preload(GFP_NOFS)) {
cond_resched();
goto retry;
}
e = radix_tree_lookup(&sbi->ino_root[type], ino); spin_lock(&im->ino_lock);
e = radix_tree_lookup(&im->ino_root, ino);
if (!e) { if (!e) {
e = kmem_cache_alloc(ino_entry_slab, GFP_ATOMIC); e = kmem_cache_alloc(ino_entry_slab, GFP_ATOMIC);
if (!e) { if (!e) {
spin_unlock(&sbi->ino_lock[type]); spin_unlock(&im->ino_lock);
radix_tree_preload_end();
goto retry; goto retry;
} }
if (radix_tree_insert(&sbi->ino_root[type], ino, e)) { if (radix_tree_insert(&im->ino_root, ino, e)) {
spin_unlock(&sbi->ino_lock[type]); spin_unlock(&im->ino_lock);
kmem_cache_free(ino_entry_slab, e); kmem_cache_free(ino_entry_slab, e);
radix_tree_preload_end();
goto retry; goto retry;
} }
memset(e, 0, sizeof(struct ino_entry)); memset(e, 0, sizeof(struct ino_entry));
e->ino = ino; e->ino = ino;
list_add_tail(&e->list, &sbi->ino_list[type]); list_add_tail(&e->list, &im->ino_list);
if (type != ORPHAN_INO)
im->ino_num++;
} }
spin_unlock(&sbi->ino_lock[type]); spin_unlock(&im->ino_lock);
radix_tree_preload_end();
} }
static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
{ {
struct inode_management *im = &sbi->im[type];
struct ino_entry *e; struct ino_entry *e;
spin_lock(&sbi->ino_lock[type]); spin_lock(&im->ino_lock);
e = radix_tree_lookup(&sbi->ino_root[type], ino); e = radix_tree_lookup(&im->ino_root, ino);
if (e) { if (e) {
list_del(&e->list); list_del(&e->list);
radix_tree_delete(&sbi->ino_root[type], ino); radix_tree_delete(&im->ino_root, ino);
if (type == ORPHAN_INO) im->ino_num--;
sbi->n_orphans--; spin_unlock(&im->ino_lock);
spin_unlock(&sbi->ino_lock[type]);
kmem_cache_free(ino_entry_slab, e); kmem_cache_free(ino_entry_slab, e);
return; return;
} }
spin_unlock(&sbi->ino_lock[type]); spin_unlock(&im->ino_lock);
} }
void add_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type) void add_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type)
@ -355,10 +380,12 @@ void remove_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type)
/* mode should be APPEND_INO or UPDATE_INO */ /* mode should be APPEND_INO or UPDATE_INO */
bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode) bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode)
{ {
struct inode_management *im = &sbi->im[mode];
struct ino_entry *e; struct ino_entry *e;
spin_lock(&sbi->ino_lock[mode]);
e = radix_tree_lookup(&sbi->ino_root[mode], ino); spin_lock(&im->ino_lock);
spin_unlock(&sbi->ino_lock[mode]); e = radix_tree_lookup(&im->ino_root, ino);
spin_unlock(&im->ino_lock);
return e ? true : false; return e ? true : false;
} }
@ -368,36 +395,42 @@ void release_dirty_inode(struct f2fs_sb_info *sbi)
int i; int i;
for (i = APPEND_INO; i <= UPDATE_INO; i++) { for (i = APPEND_INO; i <= UPDATE_INO; i++) {
spin_lock(&sbi->ino_lock[i]); struct inode_management *im = &sbi->im[i];
list_for_each_entry_safe(e, tmp, &sbi->ino_list[i], list) {
spin_lock(&im->ino_lock);
list_for_each_entry_safe(e, tmp, &im->ino_list, list) {
list_del(&e->list); list_del(&e->list);
radix_tree_delete(&sbi->ino_root[i], e->ino); radix_tree_delete(&im->ino_root, e->ino);
kmem_cache_free(ino_entry_slab, e); kmem_cache_free(ino_entry_slab, e);
im->ino_num--;
} }
spin_unlock(&sbi->ino_lock[i]); spin_unlock(&im->ino_lock);
} }
} }
int acquire_orphan_inode(struct f2fs_sb_info *sbi) int acquire_orphan_inode(struct f2fs_sb_info *sbi)
{ {
struct inode_management *im = &sbi->im[ORPHAN_INO];
int err = 0; int err = 0;
spin_lock(&sbi->ino_lock[ORPHAN_INO]); spin_lock(&im->ino_lock);
if (unlikely(sbi->n_orphans >= sbi->max_orphans)) if (unlikely(im->ino_num >= sbi->max_orphans))
err = -ENOSPC; err = -ENOSPC;
else else
sbi->n_orphans++; im->ino_num++;
spin_unlock(&sbi->ino_lock[ORPHAN_INO]); spin_unlock(&im->ino_lock);
return err; return err;
} }
void release_orphan_inode(struct f2fs_sb_info *sbi) void release_orphan_inode(struct f2fs_sb_info *sbi)
{ {
spin_lock(&sbi->ino_lock[ORPHAN_INO]); struct inode_management *im = &sbi->im[ORPHAN_INO];
f2fs_bug_on(sbi, sbi->n_orphans == 0);
sbi->n_orphans--; spin_lock(&im->ino_lock);
spin_unlock(&sbi->ino_lock[ORPHAN_INO]); f2fs_bug_on(sbi, im->ino_num == 0);
im->ino_num--;
spin_unlock(&im->ino_lock);
} }
void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
@ -460,17 +493,19 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
struct f2fs_orphan_block *orphan_blk = NULL; struct f2fs_orphan_block *orphan_blk = NULL;
unsigned int nentries = 0; unsigned int nentries = 0;
unsigned short index; unsigned short index;
unsigned short orphan_blocks = unsigned short orphan_blocks;
(unsigned short)GET_ORPHAN_BLOCKS(sbi->n_orphans);
struct page *page = NULL; struct page *page = NULL;
struct ino_entry *orphan = NULL; struct ino_entry *orphan = NULL;
struct inode_management *im = &sbi->im[ORPHAN_INO];
orphan_blocks = GET_ORPHAN_BLOCKS(im->ino_num);
for (index = 0; index < orphan_blocks; index++) for (index = 0; index < orphan_blocks; index++)
grab_meta_page(sbi, start_blk + index); grab_meta_page(sbi, start_blk + index);
index = 1; index = 1;
spin_lock(&sbi->ino_lock[ORPHAN_INO]); spin_lock(&im->ino_lock);
head = &sbi->ino_list[ORPHAN_INO]; head = &im->ino_list;
/* loop for each orphan inode entry and write them in Jornal block */ /* loop for each orphan inode entry and write them in Jornal block */
list_for_each_entry(orphan, head, list) { list_for_each_entry(orphan, head, list) {
@ -510,7 +545,7 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
} }
spin_unlock(&sbi->ino_lock[ORPHAN_INO]); spin_unlock(&im->ino_lock);
} }
static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
@ -731,6 +766,9 @@ void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi)
struct dir_inode_entry *entry; struct dir_inode_entry *entry;
struct inode *inode; struct inode *inode;
retry: retry:
if (unlikely(f2fs_cp_error(sbi)))
return;
spin_lock(&sbi->dir_inode_lock); spin_lock(&sbi->dir_inode_lock);
head = &sbi->dir_inode_list; head = &sbi->dir_inode_list;
@ -830,6 +868,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num;
nid_t last_nid = nm_i->next_scan_nid; nid_t last_nid = nm_i->next_scan_nid;
block_t start_blk; block_t start_blk;
struct page *cp_page; struct page *cp_page;
@ -889,7 +928,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
else else
clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG);
orphan_blocks = GET_ORPHAN_BLOCKS(sbi->n_orphans); orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num);
ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks + ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks +
orphan_blocks); orphan_blocks);
@ -905,7 +944,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
orphan_blocks); orphan_blocks);
} }
if (sbi->n_orphans) if (orphan_num)
set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG);
else else
clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG);
@ -940,7 +979,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
f2fs_put_page(cp_page, 1); f2fs_put_page(cp_page, 1);
} }
if (sbi->n_orphans) { if (orphan_num) {
write_orphan_inodes(sbi, start_blk); write_orphan_inodes(sbi, start_blk);
start_blk += orphan_blocks; start_blk += orphan_blocks;
} }
@ -975,6 +1014,9 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
/* Here, we only have one bio having CP pack */ /* Here, we only have one bio having CP pack */
sync_meta_pages(sbi, META_FLUSH, LONG_MAX); sync_meta_pages(sbi, META_FLUSH, LONG_MAX);
/* wait for previous submitted meta pages writeback */
wait_on_all_pages_writeback(sbi);
release_dirty_inode(sbi); release_dirty_inode(sbi);
if (unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
@ -1036,9 +1078,12 @@ void init_ino_entry_info(struct f2fs_sb_info *sbi)
int i; int i;
for (i = 0; i < MAX_INO_ENTRY; i++) { for (i = 0; i < MAX_INO_ENTRY; i++) {
INIT_RADIX_TREE(&sbi->ino_root[i], GFP_ATOMIC); struct inode_management *im = &sbi->im[i];
spin_lock_init(&sbi->ino_lock[i]);
INIT_LIST_HEAD(&sbi->ino_list[i]); INIT_RADIX_TREE(&im->ino_root, GFP_ATOMIC);
spin_lock_init(&im->ino_lock);
INIT_LIST_HEAD(&im->ino_list);
im->ino_num = 0;
} }
/* /*
@ -1047,7 +1092,6 @@ void init_ino_entry_info(struct f2fs_sb_info *sbi)
* orphan entries with the limitation one reserved segment * orphan entries with the limitation one reserved segment
* for cp pack we can have max 1020*504 orphan entries * for cp pack we can have max 1020*504 orphan entries
*/ */
sbi->n_orphans = 0;
sbi->max_orphans = (sbi->blocks_per_seg - F2FS_CP_PACKS - sbi->max_orphans = (sbi->blocks_per_seg - F2FS_CP_PACKS -
NR_CURSEG_TYPE) * F2FS_ORPHANS_PER_BLOCK; NR_CURSEG_TYPE) * F2FS_ORPHANS_PER_BLOCK;
} }

View File

@ -61,11 +61,6 @@ static void f2fs_write_end_io(struct bio *bio, int err)
dec_page_count(sbi, F2FS_WRITEBACK); dec_page_count(sbi, F2FS_WRITEBACK);
} }
if (sbi->wait_io) {
complete(sbi->wait_io);
sbi->wait_io = NULL;
}
if (!get_pages(sbi, F2FS_WRITEBACK) && if (!get_pages(sbi, F2FS_WRITEBACK) &&
!list_empty(&sbi->cp_wait.task_list)) !list_empty(&sbi->cp_wait.task_list))
wake_up(&sbi->cp_wait); wake_up(&sbi->cp_wait);
@ -95,34 +90,18 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr,
static void __submit_merged_bio(struct f2fs_bio_info *io) static void __submit_merged_bio(struct f2fs_bio_info *io)
{ {
struct f2fs_io_info *fio = &io->fio; struct f2fs_io_info *fio = &io->fio;
int rw;
if (!io->bio) if (!io->bio)
return; return;
rw = fio->rw; if (is_read_io(fio->rw))
trace_f2fs_submit_read_bio(io->sbi->sb, fio->rw,
if (is_read_io(rw)) { fio->type, io->bio);
trace_f2fs_submit_read_bio(io->sbi->sb, rw, else
fio->type, io->bio); trace_f2fs_submit_write_bio(io->sbi->sb, fio->rw,
submit_bio(rw, io->bio); fio->type, io->bio);
} else {
trace_f2fs_submit_write_bio(io->sbi->sb, rw,
fio->type, io->bio);
/*
* META_FLUSH is only from the checkpoint procedure, and we
* should wait this metadata bio for FS consistency.
*/
if (fio->type == META_FLUSH) {
DECLARE_COMPLETION_ONSTACK(wait);
io->sbi->wait_io = &wait;
submit_bio(rw, io->bio);
wait_for_completion(&wait);
} else {
submit_bio(rw, io->bio);
}
}
submit_bio(fio->rw, io->bio);
io->bio = NULL; io->bio = NULL;
} }
@ -257,9 +236,6 @@ int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index)
bool need_put = dn->inode_page ? false : true; bool need_put = dn->inode_page ? false : true;
int err; int err;
/* if inode_page exists, index should be zero */
f2fs_bug_on(F2FS_I_SB(dn->inode), !need_put && index);
err = get_dnode_of_data(dn, index, ALLOC_NODE); err = get_dnode_of_data(dn, index, ALLOC_NODE);
if (err) if (err)
return err; return err;
@ -740,14 +716,14 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
static int f2fs_read_data_page(struct file *file, struct page *page) static int f2fs_read_data_page(struct file *file, struct page *page)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
int ret; int ret = -EAGAIN;
trace_f2fs_readpage(page, DATA); trace_f2fs_readpage(page, DATA);
/* If the file has inline data, try to read it directly */ /* If the file has inline data, try to read it directly */
if (f2fs_has_inline_data(inode)) if (f2fs_has_inline_data(inode))
ret = f2fs_read_inline_data(inode, page); ret = f2fs_read_inline_data(inode, page);
else if (ret == -EAGAIN)
ret = mpage_readpage(page, get_data_block); ret = mpage_readpage(page, get_data_block);
return ret; return ret;
@ -859,10 +835,11 @@ write:
else if (has_not_enough_free_secs(sbi, 0)) else if (has_not_enough_free_secs(sbi, 0))
goto redirty_out; goto redirty_out;
err = -EAGAIN;
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
if (f2fs_has_inline_data(inode) || f2fs_may_inline(inode)) if (f2fs_has_inline_data(inode))
err = f2fs_write_inline_data(inode, page, offset); err = f2fs_write_inline_data(inode, page);
else if (err == -EAGAIN)
err = do_write_data_page(page, &fio); err = do_write_data_page(page, &fio);
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
done: done:
@ -951,7 +928,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
{ {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct page *page; struct page *page, *ipage;
pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT; pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT;
struct dnode_of_data dn; struct dnode_of_data dn;
int err = 0; int err = 0;
@ -959,45 +936,60 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
trace_f2fs_write_begin(inode, pos, len, flags); trace_f2fs_write_begin(inode, pos, len, flags);
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
repeat:
err = f2fs_convert_inline_data(inode, pos + len, NULL);
if (err)
goto fail;
/*
* We should check this at this moment to avoid deadlock on inode page
* and #0 page. The locking rule for inline_data conversion should be:
* lock_page(page #0) -> lock_page(inode_page)
*/
if (index != 0) {
err = f2fs_convert_inline_inode(inode);
if (err)
goto fail;
}
repeat:
page = grab_cache_page_write_begin(mapping, index, flags); page = grab_cache_page_write_begin(mapping, index, flags);
if (!page) { if (!page) {
err = -ENOMEM; err = -ENOMEM;
goto fail; goto fail;
} }
/* to avoid latency during memory pressure */
unlock_page(page);
*pagep = page; *pagep = page;
if (f2fs_has_inline_data(inode) && (pos + len) <= MAX_INLINE_DATA)
goto inline_data;
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = f2fs_reserve_block(&dn, index); /* check inline_data */
f2fs_unlock_op(sbi); ipage = get_node_page(sbi, inode->i_ino);
if (err) { if (IS_ERR(ipage)) {
f2fs_put_page(page, 0); err = PTR_ERR(ipage);
goto fail; goto unlock_fail;
}
inline_data:
lock_page(page);
if (unlikely(page->mapping != mapping)) {
f2fs_put_page(page, 1);
goto repeat;
} }
f2fs_wait_on_page_writeback(page, DATA); set_new_dnode(&dn, inode, ipage, ipage, 0);
if (f2fs_has_inline_data(inode)) {
if (pos + len <= MAX_INLINE_DATA) {
read_inline_data(page, ipage);
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
sync_inode_page(&dn);
goto put_next;
}
err = f2fs_convert_inline_page(&dn, page);
if (err)
goto put_fail;
}
err = f2fs_reserve_block(&dn, index);
if (err)
goto put_fail;
put_next:
f2fs_put_dnode(&dn);
f2fs_unlock_op(sbi);
if ((len == PAGE_CACHE_SIZE) || PageUptodate(page)) if ((len == PAGE_CACHE_SIZE) || PageUptodate(page))
return 0; return 0;
f2fs_wait_on_page_writeback(page, DATA);
if ((pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { if ((pos & PAGE_CACHE_MASK) >= i_size_read(inode)) {
unsigned start = pos & (PAGE_CACHE_SIZE - 1); unsigned start = pos & (PAGE_CACHE_SIZE - 1);
unsigned end = start + len; unsigned end = start + len;
@ -1010,18 +1002,10 @@ inline_data:
if (dn.data_blkaddr == NEW_ADDR) { if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE); zero_user_segment(page, 0, PAGE_CACHE_SIZE);
} else { } else {
if (f2fs_has_inline_data(inode)) { err = f2fs_submit_page_bio(sbi, page, dn.data_blkaddr,
err = f2fs_read_inline_data(inode, page); READ_SYNC);
if (err) { if (err)
page_cache_release(page); goto fail;
goto fail;
}
} else {
err = f2fs_submit_page_bio(sbi, page, dn.data_blkaddr,
READ_SYNC);
if (err)
goto fail;
}
lock_page(page); lock_page(page);
if (unlikely(!PageUptodate(page))) { if (unlikely(!PageUptodate(page))) {
@ -1038,6 +1022,12 @@ out:
SetPageUptodate(page); SetPageUptodate(page);
clear_cold_data(page); clear_cold_data(page);
return 0; return 0;
put_fail:
f2fs_put_dnode(&dn);
unlock_fail:
f2fs_unlock_op(sbi);
f2fs_put_page(page, 1);
fail: fail:
f2fs_write_failed(mapping, pos + len); f2fs_write_failed(mapping, pos + len);
return err; return err;
@ -1052,10 +1042,7 @@ static int f2fs_write_end(struct file *file,
trace_f2fs_write_end(inode, pos, len, copied); trace_f2fs_write_end(inode, pos, len, copied);
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode)) set_page_dirty(page);
register_inmem_page(inode, page);
else
set_page_dirty(page);
if (pos + copied > i_size_read(inode)) { if (pos + copied > i_size_read(inode)) {
i_size_write(inode, pos + copied); i_size_write(inode, pos + copied);
@ -1093,9 +1080,12 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
size_t count = iov_iter_count(iter); size_t count = iov_iter_count(iter);
int err; int err;
/* Let buffer I/O handle the inline data case. */ /* we don't need to use inline_data strictly */
if (f2fs_has_inline_data(inode)) if (f2fs_has_inline_data(inode)) {
return 0; err = f2fs_convert_inline_inode(inode);
if (err)
return err;
}
if (check_direct_IO(inode, rw, iter, offset)) if (check_direct_IO(inode, rw, iter, offset))
return 0; return 0;
@ -1119,6 +1109,9 @@ static void f2fs_invalidate_data_page(struct page *page, unsigned int offset,
if (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE) if (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE)
return; return;
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode))
invalidate_inmem_page(inode, page);
if (PageDirty(page)) if (PageDirty(page))
inode_dec_dirty_pages(inode); inode_dec_dirty_pages(inode);
ClearPagePrivate(page); ClearPagePrivate(page);
@ -1138,6 +1131,12 @@ static int f2fs_set_data_page_dirty(struct page *page)
trace_f2fs_set_page_dirty(page, DATA); trace_f2fs_set_page_dirty(page, DATA);
SetPageUptodate(page); SetPageUptodate(page);
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode)) {
register_inmem_page(inode, page);
return 1;
}
mark_inode_dirty(inode); mark_inode_dirty(inode);
if (!PageDirty(page)) { if (!PageDirty(page)) {
@ -1152,9 +1151,12 @@ static sector_t f2fs_bmap(struct address_space *mapping, sector_t block)
{ {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
if (f2fs_has_inline_data(inode)) /* we don't need to use inline_data strictly */
return 0; if (f2fs_has_inline_data(inode)) {
int err = f2fs_convert_inline_inode(inode);
if (err)
return err;
}
return generic_block_bmap(mapping, block, get_data_block); return generic_block_bmap(mapping, block, get_data_block);
} }

View File

@ -39,13 +39,15 @@ static void update_general_status(struct f2fs_sb_info *sbi)
si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS);
si->ndirty_dirs = sbi->n_dirty_dirs; si->ndirty_dirs = sbi->n_dirty_dirs;
si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META);
si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
si->rsvd_segs = reserved_segments(sbi); si->rsvd_segs = reserved_segments(sbi);
si->overp_segs = overprovision_segments(sbi); si->overp_segs = overprovision_segments(sbi);
si->valid_count = valid_user_blocks(sbi); si->valid_count = valid_user_blocks(sbi);
si->valid_node_count = valid_node_count(sbi); si->valid_node_count = valid_node_count(sbi);
si->valid_inode_count = valid_inode_count(sbi); si->valid_inode_count = valid_inode_count(sbi);
si->inline_inode = sbi->inline_inode; si->inline_inode = atomic_read(&sbi->inline_inode);
si->inline_dir = atomic_read(&sbi->inline_dir);
si->utilization = utilization(sbi); si->utilization = utilization(sbi);
si->free_segs = free_segments(sbi); si->free_segs = free_segments(sbi);
@ -118,6 +120,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
{ {
struct f2fs_stat_info *si = F2FS_STAT(sbi); struct f2fs_stat_info *si = F2FS_STAT(sbi);
unsigned npages; unsigned npages;
int i;
if (si->base_mem) if (si->base_mem)
goto get_cache; goto get_cache;
@ -167,8 +170,9 @@ get_cache:
si->cache_mem += npages << PAGE_CACHE_SHIFT; si->cache_mem += npages << PAGE_CACHE_SHIFT;
npages = META_MAPPING(sbi)->nrpages; npages = META_MAPPING(sbi)->nrpages;
si->cache_mem += npages << PAGE_CACHE_SHIFT; si->cache_mem += npages << PAGE_CACHE_SHIFT;
si->cache_mem += sbi->n_orphans * sizeof(struct ino_entry);
si->cache_mem += sbi->n_dirty_dirs * sizeof(struct dir_inode_entry); si->cache_mem += sbi->n_dirty_dirs * sizeof(struct dir_inode_entry);
for (i = 0; i <= UPDATE_INO; i++)
si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
} }
static int stat_show(struct seq_file *s, void *v) static int stat_show(struct seq_file *s, void *v)
@ -200,6 +204,8 @@ static int stat_show(struct seq_file *s, void *v)
si->valid_count - si->valid_node_count); si->valid_count - si->valid_node_count);
seq_printf(s, " - Inline_data Inode: %u\n", seq_printf(s, " - Inline_data Inode: %u\n",
si->inline_inode); si->inline_inode);
seq_printf(s, " - Inline_dentry Inode: %u\n",
si->inline_dir);
seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n",
si->main_area_segs, si->main_area_sections, si->main_area_segs, si->main_area_sections,
si->main_area_zones); si->main_area_zones);
@ -244,6 +250,8 @@ static int stat_show(struct seq_file *s, void *v)
seq_printf(s, "\nExtent Hit Ratio: %d / %d\n", seq_printf(s, "\nExtent Hit Ratio: %d / %d\n",
si->hit_ext, si->total_ext); si->hit_ext, si->total_ext);
seq_puts(s, "\nBalancing F2FS Async:\n"); seq_puts(s, "\nBalancing F2FS Async:\n");
seq_printf(s, " - inmem: %4d\n",
si->inmem_pages);
seq_printf(s, " - nodes: %4d in %4d\n", seq_printf(s, " - nodes: %4d in %4d\n",
si->ndirty_node, si->node_pages); si->ndirty_node, si->node_pages);
seq_printf(s, " - dents: %4d in dirs:%4d\n", seq_printf(s, " - dents: %4d in dirs:%4d\n",
@ -321,6 +329,9 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
si->sbi = sbi; si->sbi = sbi;
sbi->stat_info = si; sbi->stat_info = si;
atomic_set(&sbi->inline_inode, 0);
atomic_set(&sbi->inline_dir, 0);
mutex_lock(&f2fs_stat_mutex); mutex_lock(&f2fs_stat_mutex);
list_add_tail(&si->stat_list, &f2fs_stat_list); list_add_tail(&si->stat_list, &f2fs_stat_list);
mutex_unlock(&f2fs_stat_mutex); mutex_unlock(&f2fs_stat_mutex);

View File

@ -37,7 +37,7 @@ static unsigned int bucket_blocks(unsigned int level)
return 4; return 4;
} }
static unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { unsigned char f2fs_filetype_table[F2FS_FT_MAX] = {
[F2FS_FT_UNKNOWN] = DT_UNKNOWN, [F2FS_FT_UNKNOWN] = DT_UNKNOWN,
[F2FS_FT_REG_FILE] = DT_REG, [F2FS_FT_REG_FILE] = DT_REG,
[F2FS_FT_DIR] = DT_DIR, [F2FS_FT_DIR] = DT_DIR,
@ -59,7 +59,7 @@ static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = {
[S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK, [S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK,
}; };
static void set_de_type(struct f2fs_dir_entry *de, struct inode *inode) void set_de_type(struct f2fs_dir_entry *de, struct inode *inode)
{ {
umode_t mode = inode->i_mode; umode_t mode = inode->i_mode;
de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
@ -90,51 +90,70 @@ static bool early_match_name(size_t namelen, f2fs_hash_t namehash,
} }
static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
struct qstr *name, int *max_slots, struct qstr *name, int *max_slots,
f2fs_hash_t namehash, struct page **res_page) struct page **res_page)
{
struct f2fs_dentry_block *dentry_blk;
struct f2fs_dir_entry *de;
struct f2fs_dentry_ptr d;
dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
make_dentry_ptr(&d, (void *)dentry_blk, 1);
de = find_target_dentry(name, max_slots, &d);
if (de)
*res_page = dentry_page;
else
kunmap(dentry_page);
/*
* For the most part, it should be a bug when name_len is zero.
* We stop here for figuring out where the bugs has occurred.
*/
f2fs_bug_on(F2FS_P_SB(dentry_page), d.max < 0);
return de;
}
struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
struct f2fs_dentry_ptr *d)
{ {
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
unsigned long bit_pos = 0; unsigned long bit_pos = 0;
struct f2fs_dentry_block *dentry_blk = kmap(dentry_page); f2fs_hash_t namehash = f2fs_dentry_hash(name);
const void *dentry_bits = &dentry_blk->dentry_bitmap;
int max_len = 0; int max_len = 0;
while (bit_pos < NR_DENTRY_IN_BLOCK) { if (max_slots)
if (!test_bit_le(bit_pos, dentry_bits)) { *max_slots = 0;
while (bit_pos < d->max) {
if (!test_bit_le(bit_pos, d->bitmap)) {
if (bit_pos == 0) if (bit_pos == 0)
max_len = 1; max_len = 1;
else if (!test_bit_le(bit_pos - 1, dentry_bits)) else if (!test_bit_le(bit_pos - 1, d->bitmap))
max_len++; max_len++;
bit_pos++; bit_pos++;
continue; continue;
} }
de = &dentry_blk->dentry[bit_pos]; de = &d->dentry[bit_pos];
if (early_match_name(name->len, namehash, de)) { if (early_match_name(name->len, namehash, de) &&
if (!memcmp(dentry_blk->filename[bit_pos], !memcmp(d->filename[bit_pos], name->name, name->len))
name->name, goto found;
name->len)) {
*res_page = dentry_page; if (max_slots && *max_slots >= 0 && max_len > *max_slots) {
goto found;
}
}
if (max_len > *max_slots) {
*max_slots = max_len; *max_slots = max_len;
max_len = 0; max_len = 0;
} }
/* /* remain bug on condition */
* For the most part, it should be a bug when name_len is zero. if (unlikely(!de->name_len))
* We stop here for figuring out where the bugs has occurred. d->max = -1;
*/
f2fs_bug_on(F2FS_P_SB(dentry_page), !de->name_len);
bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
} }
de = NULL; de = NULL;
kunmap(dentry_page);
found: found:
if (max_len > *max_slots) if (max_slots && max_len > *max_slots)
*max_slots = max_len; *max_slots = max_len;
return de; return de;
} }
@ -149,7 +168,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
struct page *dentry_page; struct page *dentry_page;
struct f2fs_dir_entry *de = NULL; struct f2fs_dir_entry *de = NULL;
bool room = false; bool room = false;
int max_slots = 0; int max_slots;
f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH); f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
@ -168,8 +187,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
continue; continue;
} }
de = find_in_block(dentry_page, name, &max_slots, de = find_in_block(dentry_page, name, &max_slots, res_page);
namehash, res_page);
if (de) if (de)
break; break;
@ -201,6 +219,9 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
unsigned int max_depth; unsigned int max_depth;
unsigned int level; unsigned int level;
if (f2fs_has_inline_dentry(dir))
return find_in_inline_dir(dir, child, res_page);
if (npages == 0) if (npages == 0)
return NULL; return NULL;
@ -227,6 +248,9 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p)
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
struct f2fs_dentry_block *dentry_blk; struct f2fs_dentry_block *dentry_blk;
if (f2fs_has_inline_dentry(dir))
return f2fs_parent_inline_dir(dir, p);
page = get_lock_data_page(dir, 0); page = get_lock_data_page(dir, 0);
if (IS_ERR(page)) if (IS_ERR(page))
return NULL; return NULL;
@ -247,7 +271,7 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr)
de = f2fs_find_entry(dir, qstr, &page); de = f2fs_find_entry(dir, qstr, &page);
if (de) { if (de) {
res = le32_to_cpu(de->ino); res = le32_to_cpu(de->ino);
kunmap(page); f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
} }
@ -257,11 +281,13 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr)
void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
struct page *page, struct inode *inode) struct page *page, struct inode *inode)
{ {
enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA;
lock_page(page); lock_page(page);
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, type);
de->ino = cpu_to_le32(inode->i_ino); de->ino = cpu_to_le32(inode->i_ino);
set_de_type(de, inode); set_de_type(de, inode);
kunmap(page); if (!f2fs_has_inline_dentry(dir))
kunmap(page);
set_page_dirty(page); set_page_dirty(page);
dir->i_mtime = dir->i_ctime = CURRENT_TIME; dir->i_mtime = dir->i_ctime = CURRENT_TIME;
mark_inode_dirty(dir); mark_inode_dirty(dir);
@ -296,36 +322,48 @@ int update_dent_inode(struct inode *inode, const struct qstr *name)
return 0; return 0;
} }
void do_make_empty_dir(struct inode *inode, struct inode *parent,
struct f2fs_dentry_ptr *d)
{
struct f2fs_dir_entry *de;
de = &d->dentry[0];
de->name_len = cpu_to_le16(1);
de->hash_code = 0;
de->ino = cpu_to_le32(inode->i_ino);
memcpy(d->filename[0], ".", 1);
set_de_type(de, inode);
de = &d->dentry[1];
de->hash_code = 0;
de->name_len = cpu_to_le16(2);
de->ino = cpu_to_le32(parent->i_ino);
memcpy(d->filename[1], "..", 2);
set_de_type(de, inode);
test_and_set_bit_le(0, (void *)d->bitmap);
test_and_set_bit_le(1, (void *)d->bitmap);
}
static int make_empty_dir(struct inode *inode, static int make_empty_dir(struct inode *inode,
struct inode *parent, struct page *page) struct inode *parent, struct page *page)
{ {
struct page *dentry_page; struct page *dentry_page;
struct f2fs_dentry_block *dentry_blk; struct f2fs_dentry_block *dentry_blk;
struct f2fs_dir_entry *de; struct f2fs_dentry_ptr d;
if (f2fs_has_inline_dentry(inode))
return make_empty_inline_dir(inode, parent, page);
dentry_page = get_new_data_page(inode, page, 0, true); dentry_page = get_new_data_page(inode, page, 0, true);
if (IS_ERR(dentry_page)) if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page); return PTR_ERR(dentry_page);
dentry_blk = kmap_atomic(dentry_page); dentry_blk = kmap_atomic(dentry_page);
de = &dentry_blk->dentry[0]; make_dentry_ptr(&d, (void *)dentry_blk, 1);
de->name_len = cpu_to_le16(1); do_make_empty_dir(inode, parent, &d);
de->hash_code = 0;
de->ino = cpu_to_le32(inode->i_ino);
memcpy(dentry_blk->filename[0], ".", 1);
set_de_type(de, inode);
de = &dentry_blk->dentry[1];
de->hash_code = 0;
de->name_len = cpu_to_le16(2);
de->ino = cpu_to_le32(parent->i_ino);
memcpy(dentry_blk->filename[1], "..", 2);
set_de_type(de, inode);
test_and_set_bit_le(0, &dentry_blk->dentry_bitmap);
test_and_set_bit_le(1, &dentry_blk->dentry_bitmap);
kunmap_atomic(dentry_blk); kunmap_atomic(dentry_blk);
set_page_dirty(dentry_page); set_page_dirty(dentry_page);
@ -333,8 +371,8 @@ static int make_empty_dir(struct inode *inode,
return 0; return 0;
} }
static struct page *init_inode_metadata(struct inode *inode, struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
struct inode *dir, const struct qstr *name) const struct qstr *name, struct page *dpage)
{ {
struct page *page; struct page *page;
int err; int err;
@ -350,7 +388,7 @@ static struct page *init_inode_metadata(struct inode *inode,
goto error; goto error;
} }
err = f2fs_init_acl(inode, dir, page); err = f2fs_init_acl(inode, dir, page, dpage);
if (err) if (err)
goto put_error; goto put_error;
@ -395,7 +433,7 @@ error:
return ERR_PTR(err); return ERR_PTR(err);
} }
static void update_parent_metadata(struct inode *dir, struct inode *inode, void update_parent_metadata(struct inode *dir, struct inode *inode,
unsigned int current_depth) unsigned int current_depth)
{ {
if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
@ -417,27 +455,23 @@ static void update_parent_metadata(struct inode *dir, struct inode *inode,
clear_inode_flag(F2FS_I(inode), FI_INC_LINK); clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
} }
static int room_for_filename(struct f2fs_dentry_block *dentry_blk, int slots) int room_for_filename(const void *bitmap, int slots, int max_slots)
{ {
int bit_start = 0; int bit_start = 0;
int zero_start, zero_end; int zero_start, zero_end;
next: next:
zero_start = find_next_zero_bit_le(&dentry_blk->dentry_bitmap, zero_start = find_next_zero_bit_le(bitmap, max_slots, bit_start);
NR_DENTRY_IN_BLOCK, if (zero_start >= max_slots)
bit_start); return max_slots;
if (zero_start >= NR_DENTRY_IN_BLOCK)
return NR_DENTRY_IN_BLOCK;
zero_end = find_next_bit_le(&dentry_blk->dentry_bitmap, zero_end = find_next_bit_le(bitmap, max_slots, zero_start);
NR_DENTRY_IN_BLOCK,
zero_start);
if (zero_end - zero_start >= slots) if (zero_end - zero_start >= slots)
return zero_start; return zero_start;
bit_start = zero_end + 1; bit_start = zero_end + 1;
if (zero_end + 1 >= NR_DENTRY_IN_BLOCK) if (zero_end + 1 >= max_slots)
return NR_DENTRY_IN_BLOCK; return max_slots;
goto next; goto next;
} }
@ -463,6 +497,14 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
int err = 0; int err = 0;
int i; int i;
if (f2fs_has_inline_dentry(dir)) {
err = f2fs_add_inline_entry(dir, name, inode);
if (!err || err != -EAGAIN)
return err;
else
err = 0;
}
dentry_hash = f2fs_dentry_hash(name); dentry_hash = f2fs_dentry_hash(name);
level = 0; level = 0;
current_depth = F2FS_I(dir)->i_current_depth; current_depth = F2FS_I(dir)->i_current_depth;
@ -491,7 +533,8 @@ start:
return PTR_ERR(dentry_page); return PTR_ERR(dentry_page);
dentry_blk = kmap(dentry_page); dentry_blk = kmap(dentry_page);
bit_pos = room_for_filename(dentry_blk, slots); bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
slots, NR_DENTRY_IN_BLOCK);
if (bit_pos < NR_DENTRY_IN_BLOCK) if (bit_pos < NR_DENTRY_IN_BLOCK)
goto add_dentry; goto add_dentry;
@ -506,7 +549,7 @@ add_dentry:
f2fs_wait_on_page_writeback(dentry_page, DATA); f2fs_wait_on_page_writeback(dentry_page, DATA);
down_write(&F2FS_I(inode)->i_sem); down_write(&F2FS_I(inode)->i_sem);
page = init_inode_metadata(inode, dir, name); page = init_inode_metadata(inode, dir, name, NULL);
if (IS_ERR(page)) { if (IS_ERR(page)) {
err = PTR_ERR(page); err = PTR_ERR(page);
goto fail; goto fail;
@ -545,7 +588,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
int err = 0; int err = 0;
down_write(&F2FS_I(inode)->i_sem); down_write(&F2FS_I(inode)->i_sem);
page = init_inode_metadata(inode, dir, NULL); page = init_inode_metadata(inode, dir, NULL, NULL);
if (IS_ERR(page)) { if (IS_ERR(page)) {
err = PTR_ERR(page); err = PTR_ERR(page);
goto fail; goto fail;
@ -560,26 +603,57 @@ fail:
return err; return err;
} }
void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
down_write(&F2FS_I(inode)->i_sem);
if (S_ISDIR(inode->i_mode)) {
drop_nlink(dir);
if (page)
update_inode(dir, page);
else
update_inode_page(dir);
}
inode->i_ctime = CURRENT_TIME;
drop_nlink(inode);
if (S_ISDIR(inode->i_mode)) {
drop_nlink(inode);
i_size_write(inode, 0);
}
up_write(&F2FS_I(inode)->i_sem);
update_inode_page(inode);
if (inode->i_nlink == 0)
add_orphan_inode(sbi, inode->i_ino);
else
release_orphan_inode(sbi);
}
/* /*
* It only removes the dentry from the dentry page, corresponding name * It only removes the dentry from the dentry page, corresponding name
* entry in name page does not need to be touched during deletion. * entry in name page does not need to be touched during deletion.
*/ */
void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
struct inode *inode) struct inode *dir, struct inode *inode)
{ {
struct f2fs_dentry_block *dentry_blk; struct f2fs_dentry_block *dentry_blk;
unsigned int bit_pos; unsigned int bit_pos;
struct inode *dir = page->mapping->host;
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
int i; int i;
if (f2fs_has_inline_dentry(dir))
return f2fs_delete_inline_entry(dentry, page, dir, inode);
lock_page(page); lock_page(page);
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA);
dentry_blk = page_address(page); dentry_blk = page_address(page);
bit_pos = dentry - dentry_blk->dentry; bit_pos = dentry - dentry_blk->dentry;
for (i = 0; i < slots; i++) for (i = 0; i < slots; i++)
test_and_clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
/* Let's check and deallocate this dentry page */ /* Let's check and deallocate this dentry page */
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
@ -590,29 +664,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_ctime = dir->i_mtime = CURRENT_TIME;
if (inode) { if (inode)
struct f2fs_sb_info *sbi = F2FS_I_SB(dir); f2fs_drop_nlink(dir, inode, NULL);
down_write(&F2FS_I(inode)->i_sem);
if (S_ISDIR(inode->i_mode)) {
drop_nlink(dir);
update_inode_page(dir);
}
inode->i_ctime = CURRENT_TIME;
drop_nlink(inode);
if (S_ISDIR(inode->i_mode)) {
drop_nlink(inode);
i_size_write(inode, 0);
}
up_write(&F2FS_I(inode)->i_sem);
update_inode_page(inode);
if (inode->i_nlink == 0)
add_orphan_inode(sbi, inode->i_ino);
else
release_orphan_inode(sbi);
}
if (bit_pos == NR_DENTRY_IN_BLOCK) { if (bit_pos == NR_DENTRY_IN_BLOCK) {
truncate_hole(dir, page->index, page->index + 1); truncate_hole(dir, page->index, page->index + 1);
@ -628,9 +681,12 @@ bool f2fs_empty_dir(struct inode *dir)
unsigned long bidx; unsigned long bidx;
struct page *dentry_page; struct page *dentry_page;
unsigned int bit_pos; unsigned int bit_pos;
struct f2fs_dentry_block *dentry_blk; struct f2fs_dentry_block *dentry_blk;
unsigned long nblock = dir_blocks(dir); unsigned long nblock = dir_blocks(dir);
if (f2fs_has_inline_dentry(dir))
return f2fs_empty_inline_dir(dir);
for (bidx = 0; bidx < nblock; bidx++) { for (bidx = 0; bidx < nblock; bidx++) {
dentry_page = get_lock_data_page(dir, bidx); dentry_page = get_lock_data_page(dir, bidx);
if (IS_ERR(dentry_page)) { if (IS_ERR(dentry_page)) {
@ -640,7 +696,6 @@ bool f2fs_empty_dir(struct inode *dir)
return false; return false;
} }
dentry_blk = kmap_atomic(dentry_page); dentry_blk = kmap_atomic(dentry_page);
if (bidx == 0) if (bidx == 0)
bit_pos = 2; bit_pos = 2;
@ -659,19 +714,48 @@ bool f2fs_empty_dir(struct inode *dir)
return true; return true;
} }
bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
unsigned int start_pos)
{
unsigned char d_type = DT_UNKNOWN;
unsigned int bit_pos;
struct f2fs_dir_entry *de = NULL;
bit_pos = ((unsigned long)ctx->pos % d->max);
while (bit_pos < d->max) {
bit_pos = find_next_bit_le(d->bitmap, d->max, bit_pos);
if (bit_pos >= d->max)
break;
de = &d->dentry[bit_pos];
if (de->file_type < F2FS_FT_MAX)
d_type = f2fs_filetype_table[de->file_type];
else
d_type = DT_UNKNOWN;
if (!dir_emit(ctx, d->filename[bit_pos],
le16_to_cpu(de->name_len),
le32_to_cpu(de->ino), d_type))
return true;
bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
ctx->pos = start_pos + bit_pos;
}
return false;
}
static int f2fs_readdir(struct file *file, struct dir_context *ctx) static int f2fs_readdir(struct file *file, struct dir_context *ctx)
{ {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
unsigned long npages = dir_blocks(inode); unsigned long npages = dir_blocks(inode);
unsigned int bit_pos = 0;
struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_block *dentry_blk = NULL;
struct f2fs_dir_entry *de = NULL;
struct page *dentry_page = NULL; struct page *dentry_page = NULL;
struct file_ra_state *ra = &file->f_ra; struct file_ra_state *ra = &file->f_ra;
unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
unsigned char d_type = DT_UNKNOWN; struct f2fs_dentry_ptr d;
bit_pos = ((unsigned long)ctx->pos % NR_DENTRY_IN_BLOCK); if (f2fs_has_inline_dentry(inode))
return f2fs_read_inline_dir(file, ctx);
/* readahead for multi pages of dir */ /* readahead for multi pages of dir */
if (npages - n > 1 && !ra_has_index(ra, n)) if (npages - n > 1 && !ra_has_index(ra, n))
@ -684,28 +768,12 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
continue; continue;
dentry_blk = kmap(dentry_page); dentry_blk = kmap(dentry_page);
while (bit_pos < NR_DENTRY_IN_BLOCK) {
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
NR_DENTRY_IN_BLOCK,
bit_pos);
if (bit_pos >= NR_DENTRY_IN_BLOCK)
break;
de = &dentry_blk->dentry[bit_pos]; make_dentry_ptr(&d, (void *)dentry_blk, 1);
if (de->file_type < F2FS_FT_MAX)
d_type = f2fs_filetype_table[de->file_type]; if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK))
else goto stop;
d_type = DT_UNKNOWN;
if (!dir_emit(ctx,
dentry_blk->filename[bit_pos],
le16_to_cpu(de->name_len),
le32_to_cpu(de->ino), d_type))
goto stop;
bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
ctx->pos = n * NR_DENTRY_IN_BLOCK + bit_pos;
}
bit_pos = 0;
ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK; ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK;
kunmap(dentry_page); kunmap(dentry_page);
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);

View File

@ -46,8 +46,10 @@
#define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040 #define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040
#define F2FS_MOUNT_INLINE_XATTR 0x00000080 #define F2FS_MOUNT_INLINE_XATTR 0x00000080
#define F2FS_MOUNT_INLINE_DATA 0x00000100 #define F2FS_MOUNT_INLINE_DATA 0x00000100
#define F2FS_MOUNT_FLUSH_MERGE 0x00000200 #define F2FS_MOUNT_INLINE_DENTRY 0x00000200
#define F2FS_MOUNT_NOBARRIER 0x00000400 #define F2FS_MOUNT_FLUSH_MERGE 0x00000400
#define F2FS_MOUNT_NOBARRIER 0x00000800
#define F2FS_MOUNT_FASTBOOT 0x00001000
#define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option)
#define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option)
@ -211,6 +213,32 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
/* /*
* For INODE and NODE manager * For INODE and NODE manager
*/ */
/* for directory operations */
struct f2fs_dentry_ptr {
const void *bitmap;
struct f2fs_dir_entry *dentry;
__u8 (*filename)[F2FS_SLOT_LEN];
int max;
};
static inline void make_dentry_ptr(struct f2fs_dentry_ptr *d,
void *src, int type)
{
if (type == 1) {
struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src;
d->max = NR_DENTRY_IN_BLOCK;
d->bitmap = &t->dentry_bitmap;
d->dentry = t->dentry;
d->filename = t->filename;
} else {
struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src;
d->max = NR_INLINE_DENTRY;
d->bitmap = &t->dentry_bitmap;
d->dentry = t->dentry;
d->filename = t->filename;
}
}
/* /*
* XATTR_NODE_OFFSET stores xattrs to one node block per file keeping -1 * XATTR_NODE_OFFSET stores xattrs to one node block per file keeping -1
* as its node offset to distinguish from index node blocks. * as its node offset to distinguish from index node blocks.
@ -269,6 +297,7 @@ struct f2fs_inode_info {
struct extent_info ext; /* in-memory extent cache entry */ struct extent_info ext; /* in-memory extent cache entry */
struct dir_inode_entry *dirty_dir; /* the pointer of dirty dir */ struct dir_inode_entry *dirty_dir; /* the pointer of dirty dir */
struct radix_tree_root inmem_root; /* radix tree for inmem pages */
struct list_head inmem_pages; /* inmemory pages managed by f2fs */ struct list_head inmem_pages; /* inmemory pages managed by f2fs */
struct mutex inmem_lock; /* lock for inmemory pages */ struct mutex inmem_lock; /* lock for inmemory pages */
}; };
@ -303,7 +332,7 @@ struct f2fs_nm_info {
/* NAT cache management */ /* NAT cache management */
struct radix_tree_root nat_root;/* root of the nat entry cache */ struct radix_tree_root nat_root;/* root of the nat entry cache */
struct radix_tree_root nat_set_root;/* root of the nat set cache */ struct radix_tree_root nat_set_root;/* root of the nat set cache */
rwlock_t nat_tree_lock; /* protect nat_tree_lock */ struct rw_semaphore nat_tree_lock; /* protect nat_tree_lock */
struct list_head nat_entries; /* cached nat entry list (clean) */ struct list_head nat_entries; /* cached nat entry list (clean) */
unsigned int nat_cnt; /* the # of cached nat entries */ unsigned int nat_cnt; /* the # of cached nat entries */
unsigned int dirty_nat_cnt; /* total num of nat entries in set */ unsigned int dirty_nat_cnt; /* total num of nat entries in set */
@ -433,6 +462,7 @@ enum count_type {
F2FS_DIRTY_DENTS, F2FS_DIRTY_DENTS,
F2FS_DIRTY_NODES, F2FS_DIRTY_NODES,
F2FS_DIRTY_META, F2FS_DIRTY_META,
F2FS_INMEM_PAGES,
NR_COUNT_TYPE, NR_COUNT_TYPE,
}; };
@ -470,6 +500,14 @@ struct f2fs_bio_info {
struct rw_semaphore io_rwsem; /* blocking op for bio */ struct rw_semaphore io_rwsem; /* blocking op for bio */
}; };
/* for inner inode cache management */
struct inode_management {
struct radix_tree_root ino_root; /* ino entry array */
spinlock_t ino_lock; /* for ino entry lock */
struct list_head ino_list; /* inode list head */
unsigned long ino_num; /* number of entries */
};
struct f2fs_sb_info { struct f2fs_sb_info {
struct super_block *sb; /* pointer to VFS super block */ struct super_block *sb; /* pointer to VFS super block */
struct proc_dir_entry *s_proc; /* proc entry */ struct proc_dir_entry *s_proc; /* proc entry */
@ -488,7 +526,6 @@ struct f2fs_sb_info {
/* for bio operations */ /* for bio operations */
struct f2fs_bio_info read_io; /* for read bios */ struct f2fs_bio_info read_io; /* for read bios */
struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */
struct completion *wait_io; /* for completion bios */
/* for checkpoint */ /* for checkpoint */
struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */
@ -500,13 +537,9 @@ struct f2fs_sb_info {
bool por_doing; /* recovery is doing or not */ bool por_doing; /* recovery is doing or not */
wait_queue_head_t cp_wait; wait_queue_head_t cp_wait;
/* for inode management */ struct inode_management im[MAX_INO_ENTRY]; /* manage inode cache */
struct radix_tree_root ino_root[MAX_INO_ENTRY]; /* ino entry array */
spinlock_t ino_lock[MAX_INO_ENTRY]; /* for ino entry lock */
struct list_head ino_list[MAX_INO_ENTRY]; /* inode list head */
/* for orphan inode, use 0'th array */ /* for orphan inode, use 0'th array */
unsigned int n_orphans; /* # of orphan inodes */
unsigned int max_orphans; /* max orphan inodes */ unsigned int max_orphans; /* max orphan inodes */
/* for directory inode management */ /* for directory inode management */
@ -557,7 +590,8 @@ struct f2fs_sb_info {
unsigned int segment_count[2]; /* # of allocated segments */ unsigned int segment_count[2]; /* # of allocated segments */
unsigned int block_count[2]; /* # of allocated blocks */ unsigned int block_count[2]; /* # of allocated blocks */
int total_hit_ext, read_hit_ext; /* extent cache hit ratio */ int total_hit_ext, read_hit_ext; /* extent cache hit ratio */
int inline_inode; /* # of inline_data inodes */ atomic_t inline_inode; /* # of inline_data inodes */
atomic_t inline_dir; /* # of inline_dentry inodes */
int bg_gc; /* background gc calls */ int bg_gc; /* background gc calls */
unsigned int n_dirty_dirs; /* # of dir inodes */ unsigned int n_dirty_dirs; /* # of dir inodes */
#endif #endif
@ -988,6 +1022,13 @@ retry:
return entry; return entry;
} }
static inline void f2fs_radix_tree_insert(struct radix_tree_root *root,
unsigned long index, void *item)
{
while (radix_tree_insert(root, index, item))
cond_resched();
}
#define RAW_IS_INODE(p) ((p)->footer.nid == (p)->footer.ino) #define RAW_IS_INODE(p) ((p)->footer.nid == (p)->footer.ino)
static inline bool IS_INODE(struct page *page) static inline bool IS_INODE(struct page *page)
@ -1020,7 +1061,7 @@ static inline int f2fs_test_bit(unsigned int nr, char *addr)
return mask & *addr; return mask & *addr;
} }
static inline int f2fs_set_bit(unsigned int nr, char *addr) static inline int f2fs_test_and_set_bit(unsigned int nr, char *addr)
{ {
int mask; int mask;
int ret; int ret;
@ -1032,7 +1073,7 @@ static inline int f2fs_set_bit(unsigned int nr, char *addr)
return ret; return ret;
} }
static inline int f2fs_clear_bit(unsigned int nr, char *addr) static inline int f2fs_test_and_clear_bit(unsigned int nr, char *addr)
{ {
int mask; int mask;
int ret; int ret;
@ -1044,6 +1085,15 @@ static inline int f2fs_clear_bit(unsigned int nr, char *addr)
return ret; return ret;
} }
static inline void f2fs_change_bit(unsigned int nr, char *addr)
{
int mask;
addr += (nr >> 3);
mask = 1 << (7 - (nr & 0x07));
*addr ^= mask;
}
/* used for f2fs_inode_info->flags */ /* used for f2fs_inode_info->flags */
enum { enum {
FI_NEW_INODE, /* indicate newly allocated inode */ FI_NEW_INODE, /* indicate newly allocated inode */
@ -1057,11 +1107,13 @@ enum {
FI_NO_EXTENT, /* not to use the extent cache */ FI_NO_EXTENT, /* not to use the extent cache */
FI_INLINE_XATTR, /* used for inline xattr */ FI_INLINE_XATTR, /* used for inline xattr */
FI_INLINE_DATA, /* used for inline data*/ FI_INLINE_DATA, /* used for inline data*/
FI_INLINE_DENTRY, /* used for inline dentry */
FI_APPEND_WRITE, /* inode has appended data */ FI_APPEND_WRITE, /* inode has appended data */
FI_UPDATE_WRITE, /* inode has in-place-update data */ FI_UPDATE_WRITE, /* inode has in-place-update data */
FI_NEED_IPU, /* used for ipu per file */ FI_NEED_IPU, /* used for ipu per file */
FI_ATOMIC_FILE, /* indicate atomic file */ FI_ATOMIC_FILE, /* indicate atomic file */
FI_VOLATILE_FILE, /* indicate volatile file */ FI_VOLATILE_FILE, /* indicate volatile file */
FI_DATA_EXIST, /* indicate data exists */
}; };
static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
@ -1087,15 +1139,6 @@ static inline void set_acl_inode(struct f2fs_inode_info *fi, umode_t mode)
set_inode_flag(fi, FI_ACL_MODE); set_inode_flag(fi, FI_ACL_MODE);
} }
static inline int cond_clear_inode_flag(struct f2fs_inode_info *fi, int flag)
{
if (is_inode_flag_set(fi, FI_ACL_MODE)) {
clear_inode_flag(fi, FI_ACL_MODE);
return 1;
}
return 0;
}
static inline void get_inline_info(struct f2fs_inode_info *fi, static inline void get_inline_info(struct f2fs_inode_info *fi,
struct f2fs_inode *ri) struct f2fs_inode *ri)
{ {
@ -1103,6 +1146,10 @@ static inline void get_inline_info(struct f2fs_inode_info *fi,
set_inode_flag(fi, FI_INLINE_XATTR); set_inode_flag(fi, FI_INLINE_XATTR);
if (ri->i_inline & F2FS_INLINE_DATA) if (ri->i_inline & F2FS_INLINE_DATA)
set_inode_flag(fi, FI_INLINE_DATA); set_inode_flag(fi, FI_INLINE_DATA);
if (ri->i_inline & F2FS_INLINE_DENTRY)
set_inode_flag(fi, FI_INLINE_DENTRY);
if (ri->i_inline & F2FS_DATA_EXIST)
set_inode_flag(fi, FI_DATA_EXIST);
} }
static inline void set_raw_inline(struct f2fs_inode_info *fi, static inline void set_raw_inline(struct f2fs_inode_info *fi,
@ -1114,6 +1161,10 @@ static inline void set_raw_inline(struct f2fs_inode_info *fi,
ri->i_inline |= F2FS_INLINE_XATTR; ri->i_inline |= F2FS_INLINE_XATTR;
if (is_inode_flag_set(fi, FI_INLINE_DATA)) if (is_inode_flag_set(fi, FI_INLINE_DATA))
ri->i_inline |= F2FS_INLINE_DATA; ri->i_inline |= F2FS_INLINE_DATA;
if (is_inode_flag_set(fi, FI_INLINE_DENTRY))
ri->i_inline |= F2FS_INLINE_DENTRY;
if (is_inode_flag_set(fi, FI_DATA_EXIST))
ri->i_inline |= F2FS_DATA_EXIST;
} }
static inline int f2fs_has_inline_xattr(struct inode *inode) static inline int f2fs_has_inline_xattr(struct inode *inode)
@ -1148,6 +1199,17 @@ static inline int f2fs_has_inline_data(struct inode *inode)
return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DATA); return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DATA);
} }
static inline void f2fs_clear_inline_inode(struct inode *inode)
{
clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
clear_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
}
static inline int f2fs_exist_data(struct inode *inode)
{
return is_inode_flag_set(F2FS_I(inode), FI_DATA_EXIST);
}
static inline bool f2fs_is_atomic_file(struct inode *inode) static inline bool f2fs_is_atomic_file(struct inode *inode)
{ {
return is_inode_flag_set(F2FS_I(inode), FI_ATOMIC_FILE); return is_inode_flag_set(F2FS_I(inode), FI_ATOMIC_FILE);
@ -1164,6 +1226,23 @@ static inline void *inline_data_addr(struct page *page)
return (void *)&(ri->i_addr[1]); return (void *)&(ri->i_addr[1]);
} }
static inline int f2fs_has_inline_dentry(struct inode *inode)
{
return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DENTRY);
}
static inline void *inline_dentry_addr(struct page *page)
{
struct f2fs_inode *ri = F2FS_INODE(page);
return (void *)&(ri->i_addr[1]);
}
static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page)
{
if (!f2fs_has_inline_dentry(dir))
kunmap(page);
}
static inline int f2fs_readonly(struct super_block *sb) static inline int f2fs_readonly(struct super_block *sb)
{ {
return sb->s_flags & MS_RDONLY; return sb->s_flags & MS_RDONLY;
@ -1224,6 +1303,19 @@ struct dentry *f2fs_get_parent(struct dentry *child);
/* /*
* dir.c * dir.c
*/ */
extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
void set_de_type(struct f2fs_dir_entry *, struct inode *);
struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *,
struct f2fs_dentry_ptr *);
bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
unsigned int);
void do_make_empty_dir(struct inode *, struct inode *,
struct f2fs_dentry_ptr *);
struct page *init_inode_metadata(struct inode *, struct inode *,
const struct qstr *, struct page *);
void update_parent_metadata(struct inode *, struct inode *, unsigned int);
int room_for_filename(const void *, int, int);
void f2fs_drop_nlink(struct inode *, struct inode *, struct page *);
struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *, struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *,
struct page **); struct page **);
struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **);
@ -1232,7 +1324,8 @@ void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
struct page *, struct inode *); struct page *, struct inode *);
int update_dent_inode(struct inode *, const struct qstr *); int update_dent_inode(struct inode *, const struct qstr *);
int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *); int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *);
void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *,
struct inode *);
int f2fs_do_tmpfile(struct inode *, struct inode *); int f2fs_do_tmpfile(struct inode *, struct inode *);
int f2fs_make_empty(struct inode *, struct inode *); int f2fs_make_empty(struct inode *, struct inode *);
bool f2fs_empty_dir(struct inode *); bool f2fs_empty_dir(struct inode *);
@ -1296,6 +1389,7 @@ void destroy_node_manager_caches(void);
* segment.c * segment.c
*/ */
void register_inmem_page(struct inode *, struct page *); void register_inmem_page(struct inode *, struct page *);
void invalidate_inmem_page(struct inode *, struct page *);
void commit_inmem_pages(struct inode *, bool); void commit_inmem_pages(struct inode *, bool);
void f2fs_balance_fs(struct f2fs_sb_info *); void f2fs_balance_fs(struct f2fs_sb_info *);
void f2fs_balance_fs_bg(struct f2fs_sb_info *); void f2fs_balance_fs_bg(struct f2fs_sb_info *);
@ -1337,8 +1431,8 @@ void destroy_segment_manager_caches(void);
*/ */
struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
struct page *get_meta_page_ra(struct f2fs_sb_info *, pgoff_t);
int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int); int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int);
void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t);
long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
void add_dirty_inode(struct f2fs_sb_info *, nid_t, int type); void add_dirty_inode(struct f2fs_sb_info *, nid_t, int type);
void remove_dirty_inode(struct f2fs_sb_info *, nid_t, int type); void remove_dirty_inode(struct f2fs_sb_info *, nid_t, int type);
@ -1405,7 +1499,7 @@ struct f2fs_stat_info {
int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta; int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta;
int nats, sits, fnids; int nats, sits, fnids;
int total_count, utilization; int total_count, utilization;
int bg_gc, inline_inode; int bg_gc, inline_inode, inline_dir, inmem_pages;
unsigned int valid_count, valid_node_count, valid_inode_count; unsigned int valid_count, valid_node_count, valid_inode_count;
unsigned int bimodal, avg_vblocks; unsigned int bimodal, avg_vblocks;
int util_free, util_valid, util_invalid; int util_free, util_valid, util_invalid;
@ -1438,14 +1532,23 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)
#define stat_inc_inline_inode(inode) \ #define stat_inc_inline_inode(inode) \
do { \ do { \
if (f2fs_has_inline_data(inode)) \ if (f2fs_has_inline_data(inode)) \
((F2FS_I_SB(inode))->inline_inode++); \ (atomic_inc(&F2FS_I_SB(inode)->inline_inode)); \
} while (0) } while (0)
#define stat_dec_inline_inode(inode) \ #define stat_dec_inline_inode(inode) \
do { \ do { \
if (f2fs_has_inline_data(inode)) \ if (f2fs_has_inline_data(inode)) \
((F2FS_I_SB(inode))->inline_inode--); \ (atomic_dec(&F2FS_I_SB(inode)->inline_inode)); \
} while (0)
#define stat_inc_inline_dir(inode) \
do { \
if (f2fs_has_inline_dentry(inode)) \
(atomic_inc(&F2FS_I_SB(inode)->inline_dir)); \
} while (0)
#define stat_dec_inline_dir(inode) \
do { \
if (f2fs_has_inline_dentry(inode)) \
(atomic_dec(&F2FS_I_SB(inode)->inline_dir)); \
} while (0) } while (0)
#define stat_inc_seg_type(sbi, curseg) \ #define stat_inc_seg_type(sbi, curseg) \
((sbi)->segment_count[(curseg)->alloc_type]++) ((sbi)->segment_count[(curseg)->alloc_type]++)
#define stat_inc_block_count(sbi, curseg) \ #define stat_inc_block_count(sbi, curseg) \
@ -1492,6 +1595,8 @@ void f2fs_destroy_root_stats(void);
#define stat_inc_read_hit(sb) #define stat_inc_read_hit(sb)
#define stat_inc_inline_inode(inode) #define stat_inc_inline_inode(inode)
#define stat_dec_inline_inode(inode) #define stat_dec_inline_inode(inode)
#define stat_inc_inline_dir(inode)
#define stat_dec_inline_dir(inode)
#define stat_inc_seg_type(sbi, curseg) #define stat_inc_seg_type(sbi, curseg)
#define stat_inc_block_count(sbi, curseg) #define stat_inc_block_count(sbi, curseg)
#define stat_inc_seg_count(si, type) #define stat_inc_seg_count(si, type)
@ -1519,9 +1624,20 @@ extern const struct inode_operations f2fs_special_inode_operations;
* inline.c * inline.c
*/ */
bool f2fs_may_inline(struct inode *); bool f2fs_may_inline(struct inode *);
void read_inline_data(struct page *, struct page *);
int f2fs_read_inline_data(struct inode *, struct page *); int f2fs_read_inline_data(struct inode *, struct page *);
int f2fs_convert_inline_data(struct inode *, pgoff_t, struct page *); int f2fs_convert_inline_page(struct dnode_of_data *, struct page *);
int f2fs_write_inline_data(struct inode *, struct page *, unsigned int); int f2fs_convert_inline_inode(struct inode *);
void truncate_inline_data(struct inode *, u64); int f2fs_write_inline_data(struct inode *, struct page *);
void truncate_inline_data(struct page *, u64);
bool recover_inline_data(struct inode *, struct page *); bool recover_inline_data(struct inode *, struct page *);
struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *,
struct page **);
struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *);
void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *,
struct inode *, struct inode *);
bool f2fs_empty_inline_dir(struct inode *);
int f2fs_read_inline_dir(struct file *, struct dir_context *);
#endif #endif

View File

@ -41,18 +41,18 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
sb_start_pagefault(inode->i_sb); sb_start_pagefault(inode->i_sb);
/* force to convert with normal data indices */ f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
err = f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, page);
if (err)
goto out;
/* block allocation */ /* block allocation */
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = f2fs_reserve_block(&dn, page->index); err = f2fs_reserve_block(&dn, page->index);
f2fs_unlock_op(sbi); if (err) {
if (err) f2fs_unlock_op(sbi);
goto out; goto out;
}
f2fs_put_dnode(&dn);
f2fs_unlock_op(sbi);
file_update_time(vma->vm_file); file_update_time(vma->vm_file);
lock_page(page); lock_page(page);
@ -130,10 +130,45 @@ static inline bool need_do_checkpoint(struct inode *inode)
need_cp = true; need_cp = true;
else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi))) else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi)))
need_cp = true; need_cp = true;
else if (test_opt(sbi, FASTBOOT))
need_cp = true;
else if (sbi->active_logs == 2)
need_cp = true;
return need_cp; return need_cp;
} }
static bool need_inode_page_update(struct f2fs_sb_info *sbi, nid_t ino)
{
struct page *i = find_get_page(NODE_MAPPING(sbi), ino);
bool ret = false;
/* But we need to avoid that there are some inode updates */
if ((i && PageDirty(i)) || need_inode_block_update(sbi, ino))
ret = true;
f2fs_put_page(i, 0);
return ret;
}
static void try_to_fix_pino(struct inode *inode)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
nid_t pino;
down_write(&fi->i_sem);
fi->xattr_ver = 0;
if (file_wrong_pino(inode) && inode->i_nlink == 1 &&
get_parent_ino(inode, &pino)) {
fi->i_pino = pino;
file_got_pino(inode);
up_write(&fi->i_sem);
mark_inode_dirty_sync(inode);
f2fs_write_inode(inode, NULL);
} else {
up_write(&fi->i_sem);
}
}
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{ {
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
@ -164,19 +199,21 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
return ret; return ret;
} }
/* if the inode is dirty, let's recover all the time */
if (!datasync && is_inode_flag_set(fi, FI_DIRTY_INODE)) {
update_inode_page(inode);
goto go_write;
}
/* /*
* if there is no written data, don't waste time to write recovery info. * if there is no written data, don't waste time to write recovery info.
*/ */
if (!is_inode_flag_set(fi, FI_APPEND_WRITE) && if (!is_inode_flag_set(fi, FI_APPEND_WRITE) &&
!exist_written_data(sbi, ino, APPEND_INO)) { !exist_written_data(sbi, ino, APPEND_INO)) {
struct page *i = find_get_page(NODE_MAPPING(sbi), ino);
/* But we need to avoid that there are some inode updates */ /* it may call write_inode just prior to fsync */
if ((i && PageDirty(i)) || need_inode_block_update(sbi, ino)) { if (need_inode_page_update(sbi, ino))
f2fs_put_page(i, 0);
goto go_write; goto go_write;
}
f2fs_put_page(i, 0);
if (is_inode_flag_set(fi, FI_UPDATE_WRITE) || if (is_inode_flag_set(fi, FI_UPDATE_WRITE) ||
exist_written_data(sbi, ino, UPDATE_INO)) exist_written_data(sbi, ino, UPDATE_INO))
@ -196,49 +233,36 @@ go_write:
up_read(&fi->i_sem); up_read(&fi->i_sem);
if (need_cp) { if (need_cp) {
nid_t pino;
/* all the dirty node pages should be flushed for POR */ /* all the dirty node pages should be flushed for POR */
ret = f2fs_sync_fs(inode->i_sb, 1); ret = f2fs_sync_fs(inode->i_sb, 1);
down_write(&fi->i_sem); /*
F2FS_I(inode)->xattr_ver = 0; * We've secured consistency through sync_fs. Following pino
if (file_wrong_pino(inode) && inode->i_nlink == 1 && * will be used only for fsynced inodes after checkpoint.
get_parent_ino(inode, &pino)) { */
F2FS_I(inode)->i_pino = pino; try_to_fix_pino(inode);
file_got_pino(inode); goto out;
up_write(&fi->i_sem);
mark_inode_dirty_sync(inode);
ret = f2fs_write_inode(inode, NULL);
if (ret)
goto out;
} else {
up_write(&fi->i_sem);
}
} else {
sync_nodes:
sync_node_pages(sbi, ino, &wbc);
if (need_inode_block_update(sbi, ino)) {
mark_inode_dirty_sync(inode);
ret = f2fs_write_inode(inode, NULL);
if (ret)
goto out;
goto sync_nodes;
}
ret = wait_on_node_pages_writeback(sbi, ino);
if (ret)
goto out;
/* once recovery info is written, don't need to tack this */
remove_dirty_inode(sbi, ino, APPEND_INO);
clear_inode_flag(fi, FI_APPEND_WRITE);
flush_out:
remove_dirty_inode(sbi, ino, UPDATE_INO);
clear_inode_flag(fi, FI_UPDATE_WRITE);
ret = f2fs_issue_flush(F2FS_I_SB(inode));
} }
sync_nodes:
sync_node_pages(sbi, ino, &wbc);
if (need_inode_block_update(sbi, ino)) {
mark_inode_dirty_sync(inode);
f2fs_write_inode(inode, NULL);
goto sync_nodes;
}
ret = wait_on_node_pages_writeback(sbi, ino);
if (ret)
goto out;
/* once recovery info is written, don't need to tack this */
remove_dirty_inode(sbi, ino, APPEND_INO);
clear_inode_flag(fi, FI_APPEND_WRITE);
flush_out:
remove_dirty_inode(sbi, ino, UPDATE_INO);
clear_inode_flag(fi, FI_UPDATE_WRITE);
ret = f2fs_issue_flush(sbi);
out: out:
trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
return ret; return ret;
@ -296,7 +320,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
goto fail; goto fail;
/* handle inline data case */ /* handle inline data case */
if (f2fs_has_inline_data(inode)) { if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) {
if (whence == SEEK_HOLE) if (whence == SEEK_HOLE)
data_ofs = isize; data_ofs = isize;
goto found; goto found;
@ -374,6 +398,15 @@ static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence)
static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
{ {
struct inode *inode = file_inode(file);
/* we don't need to use inline_data strictly */
if (f2fs_has_inline_data(inode)) {
int err = f2fs_convert_inline_inode(inode);
if (err)
return err;
}
file_accessed(file); file_accessed(file);
vma->vm_ops = &f2fs_file_vm_ops; vma->vm_ops = &f2fs_file_vm_ops;
return 0; return 0;
@ -415,20 +448,17 @@ void truncate_data_blocks(struct dnode_of_data *dn)
truncate_data_blocks_range(dn, ADDRS_PER_BLOCK); truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
} }
static void truncate_partial_data_page(struct inode *inode, u64 from) static int truncate_partial_data_page(struct inode *inode, u64 from)
{ {
unsigned offset = from & (PAGE_CACHE_SIZE - 1); unsigned offset = from & (PAGE_CACHE_SIZE - 1);
struct page *page; struct page *page;
if (f2fs_has_inline_data(inode))
return truncate_inline_data(inode, from);
if (!offset) if (!offset)
return; return 0;
page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false); page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false);
if (IS_ERR(page)) if (IS_ERR(page))
return; return 0;
lock_page(page); lock_page(page);
if (unlikely(!PageUptodate(page) || if (unlikely(!PageUptodate(page) ||
@ -438,9 +468,9 @@ static void truncate_partial_data_page(struct inode *inode, u64 from)
f2fs_wait_on_page_writeback(page, DATA); f2fs_wait_on_page_writeback(page, DATA);
zero_user(page, offset, PAGE_CACHE_SIZE - offset); zero_user(page, offset, PAGE_CACHE_SIZE - offset);
set_page_dirty(page); set_page_dirty(page);
out: out:
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
return 0;
} }
int truncate_blocks(struct inode *inode, u64 from, bool lock) int truncate_blocks(struct inode *inode, u64 from, bool lock)
@ -450,27 +480,33 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
struct dnode_of_data dn; struct dnode_of_data dn;
pgoff_t free_from; pgoff_t free_from;
int count = 0, err = 0; int count = 0, err = 0;
struct page *ipage;
trace_f2fs_truncate_blocks_enter(inode, from); trace_f2fs_truncate_blocks_enter(inode, from);
if (f2fs_has_inline_data(inode))
goto done;
free_from = (pgoff_t) free_from = (pgoff_t)
((from + blocksize - 1) >> (sbi->log_blocksize)); ((from + blocksize - 1) >> (sbi->log_blocksize));
if (lock) if (lock)
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0); ipage = get_node_page(sbi, inode->i_ino);
if (IS_ERR(ipage)) {
err = PTR_ERR(ipage);
goto out;
}
if (f2fs_has_inline_data(inode)) {
f2fs_put_page(ipage, 1);
goto out;
}
set_new_dnode(&dn, inode, ipage, NULL, 0);
err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE); err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE);
if (err) { if (err) {
if (err == -ENOENT) if (err == -ENOENT)
goto free_next; goto free_next;
if (lock) goto out;
f2fs_unlock_op(sbi);
trace_f2fs_truncate_blocks_exit(inode, err);
return err;
} }
count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
@ -486,11 +522,13 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
free_next: free_next:
err = truncate_inode_blocks(inode, free_from); err = truncate_inode_blocks(inode, free_from);
out:
if (lock) if (lock)
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
done:
/* lastly zero out the first data page */ /* lastly zero out the first data page */
truncate_partial_data_page(inode, from); if (!err)
err = truncate_partial_data_page(inode, from);
trace_f2fs_truncate_blocks_exit(inode, err); trace_f2fs_truncate_blocks_exit(inode, err);
return err; return err;
@ -504,6 +542,12 @@ void f2fs_truncate(struct inode *inode)
trace_f2fs_truncate(inode); trace_f2fs_truncate(inode);
/* we should check inline_data size */
if (f2fs_has_inline_data(inode) && !f2fs_may_inline(inode)) {
if (f2fs_convert_inline_inode(inode))
return;
}
if (!truncate_blocks(inode, i_size_read(inode), true)) { if (!truncate_blocks(inode, i_size_read(inode), true)) {
inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode); mark_inode_dirty(inode);
@ -561,10 +605,6 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
return err; return err;
if (attr->ia_valid & ATTR_SIZE) { if (attr->ia_valid & ATTR_SIZE) {
err = f2fs_convert_inline_data(inode, attr->ia_size, NULL);
if (err)
return err;
if (attr->ia_size != i_size_read(inode)) { if (attr->ia_size != i_size_read(inode)) {
truncate_setsize(inode, attr->ia_size); truncate_setsize(inode, attr->ia_size);
f2fs_truncate(inode); f2fs_truncate(inode);
@ -665,9 +705,11 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
if (offset >= inode->i_size) if (offset >= inode->i_size)
return ret; return ret;
ret = f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, NULL); if (f2fs_has_inline_data(inode)) {
if (ret) ret = f2fs_convert_inline_inode(inode);
return ret; if (ret)
return ret;
}
pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
@ -721,9 +763,11 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
if (ret) if (ret)
return ret; return ret;
ret = f2fs_convert_inline_data(inode, offset + len, NULL); if (f2fs_has_inline_data(inode)) {
if (ret) ret = f2fs_convert_inline_inode(inode);
return ret; if (ret)
return ret;
}
pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
@ -874,7 +918,15 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
return f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, NULL); return f2fs_convert_inline_inode(inode);
}
static int f2fs_release_file(struct inode *inode, struct file *filp)
{
/* some remained atomic pages should discarded */
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode))
commit_inmem_pages(inode, true);
return 0;
} }
static int f2fs_ioc_commit_atomic_write(struct file *filp) static int f2fs_ioc_commit_atomic_write(struct file *filp)
@ -908,7 +960,8 @@ static int f2fs_ioc_start_volatile_write(struct file *filp)
return -EACCES; return -EACCES;
set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
return 0;
return f2fs_convert_inline_inode(inode);
} }
static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
@ -985,6 +1038,7 @@ const struct file_operations f2fs_file_operations = {
.read_iter = generic_file_read_iter, .read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter, .write_iter = generic_file_write_iter,
.open = generic_file_open, .open = generic_file_open,
.release = f2fs_release_file,
.mmap = f2fs_file_mmap, .mmap = f2fs_file_mmap,
.fsync = f2fs_sync_file, .fsync = f2fs_sync_file,
.fallocate = f2fs_fallocate, .fallocate = f2fs_fallocate,

View File

@ -96,8 +96,6 @@ int start_gc_thread(struct f2fs_sb_info *sbi)
dev_t dev = sbi->sb->s_bdev->bd_dev; dev_t dev = sbi->sb->s_bdev->bd_dev;
int err = 0; int err = 0;
if (!test_opt(sbi, BG_GC))
goto out;
gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL);
if (!gc_th) { if (!gc_th) {
err = -ENOMEM; err = -ENOMEM;
@ -340,34 +338,39 @@ static const struct victim_selection default_v_ops = {
.get_victim = get_victim_by_default, .get_victim = get_victim_by_default,
}; };
static struct inode *find_gc_inode(nid_t ino, struct list_head *ilist) static struct inode *find_gc_inode(struct gc_inode_list *gc_list, nid_t ino)
{ {
struct inode_entry *ie; struct inode_entry *ie;
list_for_each_entry(ie, ilist, list) ie = radix_tree_lookup(&gc_list->iroot, ino);
if (ie->inode->i_ino == ino) if (ie)
return ie->inode; return ie->inode;
return NULL; return NULL;
} }
static void add_gc_inode(struct inode *inode, struct list_head *ilist) static void add_gc_inode(struct gc_inode_list *gc_list, struct inode *inode)
{ {
struct inode_entry *new_ie; struct inode_entry *new_ie;
if (inode == find_gc_inode(inode->i_ino, ilist)) { if (inode == find_gc_inode(gc_list, inode->i_ino)) {
iput(inode); iput(inode);
return; return;
} }
new_ie = f2fs_kmem_cache_alloc(winode_slab, GFP_NOFS); new_ie = f2fs_kmem_cache_alloc(winode_slab, GFP_NOFS);
new_ie->inode = inode; new_ie->inode = inode;
list_add_tail(&new_ie->list, ilist); retry:
if (radix_tree_insert(&gc_list->iroot, inode->i_ino, new_ie)) {
cond_resched();
goto retry;
}
list_add_tail(&new_ie->list, &gc_list->ilist);
} }
static void put_gc_inode(struct list_head *ilist) static void put_gc_inode(struct gc_inode_list *gc_list)
{ {
struct inode_entry *ie, *next_ie; struct inode_entry *ie, *next_ie;
list_for_each_entry_safe(ie, next_ie, ilist, list) { list_for_each_entry_safe(ie, next_ie, &gc_list->ilist, list) {
radix_tree_delete(&gc_list->iroot, ie->inode->i_ino);
iput(ie->inode); iput(ie->inode);
list_del(&ie->list); list_del(&ie->list);
kmem_cache_free(winode_slab, ie); kmem_cache_free(winode_slab, ie);
@ -553,7 +556,7 @@ out:
* the victim data block is ignored. * the victim data block is ignored.
*/ */
static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
struct list_head *ilist, unsigned int segno, int gc_type) struct gc_inode_list *gc_list, unsigned int segno, int gc_type)
{ {
struct super_block *sb = sbi->sb; struct super_block *sb = sbi->sb;
struct f2fs_summary *entry; struct f2fs_summary *entry;
@ -605,27 +608,27 @@ next_step:
data_page = find_data_page(inode, data_page = find_data_page(inode,
start_bidx + ofs_in_node, false); start_bidx + ofs_in_node, false);
if (IS_ERR(data_page)) if (IS_ERR(data_page)) {
goto next_iput; iput(inode);
continue;
}
f2fs_put_page(data_page, 0); f2fs_put_page(data_page, 0);
add_gc_inode(inode, ilist); add_gc_inode(gc_list, inode);
} else { continue;
inode = find_gc_inode(dni.ino, ilist); }
if (inode) {
start_bidx = start_bidx_of_node(nofs, /* phase 3 */
F2FS_I(inode)); inode = find_gc_inode(gc_list, dni.ino);
data_page = get_lock_data_page(inode, if (inode) {
start_bidx + ofs_in_node); start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
if (IS_ERR(data_page)) data_page = get_lock_data_page(inode,
continue; start_bidx + ofs_in_node);
move_data_page(inode, data_page, gc_type); if (IS_ERR(data_page))
stat_inc_data_blk_count(sbi, 1); continue;
} move_data_page(inode, data_page, gc_type);
stat_inc_data_blk_count(sbi, 1);
} }
continue;
next_iput:
iput(inode);
} }
if (++phase < 4) if (++phase < 4)
@ -646,18 +649,20 @@ next_iput:
} }
static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
int gc_type, int type) int gc_type)
{ {
struct sit_info *sit_i = SIT_I(sbi); struct sit_info *sit_i = SIT_I(sbi);
int ret; int ret;
mutex_lock(&sit_i->sentry_lock); mutex_lock(&sit_i->sentry_lock);
ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type, type, LFS); ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type,
NO_CHECK_TYPE, LFS);
mutex_unlock(&sit_i->sentry_lock); mutex_unlock(&sit_i->sentry_lock);
return ret; return ret;
} }
static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
struct list_head *ilist, int gc_type) struct gc_inode_list *gc_list, int gc_type)
{ {
struct page *sum_page; struct page *sum_page;
struct f2fs_summary_block *sum; struct f2fs_summary_block *sum;
@ -675,7 +680,7 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
gc_node_segment(sbi, sum->entries, segno, gc_type); gc_node_segment(sbi, sum->entries, segno, gc_type);
break; break;
case SUM_TYPE_DATA: case SUM_TYPE_DATA:
gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); gc_data_segment(sbi, sum->entries, gc_list, segno, gc_type);
break; break;
} }
blk_finish_plug(&plug); blk_finish_plug(&plug);
@ -688,16 +693,18 @@ static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
int f2fs_gc(struct f2fs_sb_info *sbi) int f2fs_gc(struct f2fs_sb_info *sbi)
{ {
struct list_head ilist;
unsigned int segno, i; unsigned int segno, i;
int gc_type = BG_GC; int gc_type = BG_GC;
int nfree = 0; int nfree = 0;
int ret = -1; int ret = -1;
struct cp_control cpc = { struct cp_control cpc;
.reason = CP_SYNC, struct gc_inode_list gc_list = {
.ilist = LIST_HEAD_INIT(gc_list.ilist),
.iroot = RADIX_TREE_INIT(GFP_NOFS),
}; };
INIT_LIST_HEAD(&ilist); cpc.reason = test_opt(sbi, FASTBOOT) ? CP_UMOUNT : CP_SYNC;
gc_more: gc_more:
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
goto stop; goto stop;
@ -709,7 +716,7 @@ gc_more:
write_checkpoint(sbi, &cpc); write_checkpoint(sbi, &cpc);
} }
if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) if (!__get_victim(sbi, &segno, gc_type))
goto stop; goto stop;
ret = 0; ret = 0;
@ -719,7 +726,7 @@ gc_more:
META_SSA); META_SSA);
for (i = 0; i < sbi->segs_per_sec; i++) for (i = 0; i < sbi->segs_per_sec; i++)
do_garbage_collect(sbi, segno + i, &ilist, gc_type); do_garbage_collect(sbi, segno + i, &gc_list, gc_type);
if (gc_type == FG_GC) { if (gc_type == FG_GC) {
sbi->cur_victim_sec = NULL_SEGNO; sbi->cur_victim_sec = NULL_SEGNO;
@ -735,7 +742,7 @@ gc_more:
stop: stop:
mutex_unlock(&sbi->gc_mutex); mutex_unlock(&sbi->gc_mutex);
put_gc_inode(&ilist); put_gc_inode(&gc_list);
return ret; return ret;
} }

View File

@ -40,6 +40,11 @@ struct inode_entry {
struct inode *inode; struct inode *inode;
}; };
struct gc_inode_list {
struct list_head ilist;
struct radix_tree_root iroot;
};
/* /*
* inline functions * inline functions
*/ */

View File

@ -15,35 +15,44 @@
bool f2fs_may_inline(struct inode *inode) bool f2fs_may_inline(struct inode *inode)
{ {
block_t nr_blocks;
loff_t i_size;
if (!test_opt(F2FS_I_SB(inode), INLINE_DATA)) if (!test_opt(F2FS_I_SB(inode), INLINE_DATA))
return false; return false;
if (f2fs_is_atomic_file(inode)) if (f2fs_is_atomic_file(inode))
return false; return false;
nr_blocks = F2FS_I(inode)->i_xattr_nid ? 3 : 2; if (!S_ISREG(inode->i_mode))
if (inode->i_blocks > nr_blocks)
return false; return false;
i_size = i_size_read(inode); if (i_size_read(inode) > MAX_INLINE_DATA)
if (i_size > MAX_INLINE_DATA)
return false; return false;
return true; return true;
} }
void read_inline_data(struct page *page, struct page *ipage)
{
void *src_addr, *dst_addr;
if (PageUptodate(page))
return;
f2fs_bug_on(F2FS_P_SB(page), page->index);
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
/* Copy the whole inline data block */
src_addr = inline_data_addr(ipage);
dst_addr = kmap_atomic(page);
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
flush_dcache_page(page);
kunmap_atomic(dst_addr);
SetPageUptodate(page);
}
int f2fs_read_inline_data(struct inode *inode, struct page *page) int f2fs_read_inline_data(struct inode *inode, struct page *page)
{ {
struct page *ipage; struct page *ipage;
void *src_addr, *dst_addr;
if (page->index) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
goto out;
}
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
if (IS_ERR(ipage)) { if (IS_ERR(ipage)) {
@ -51,112 +60,116 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
return PTR_ERR(ipage); return PTR_ERR(ipage);
} }
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); if (!f2fs_has_inline_data(inode)) {
f2fs_put_page(ipage, 1);
return -EAGAIN;
}
/* Copy the whole inline data block */ if (page->index)
src_addr = inline_data_addr(ipage); zero_user_segment(page, 0, PAGE_CACHE_SIZE);
dst_addr = kmap(page); else
memcpy(dst_addr, src_addr, MAX_INLINE_DATA); read_inline_data(page, ipage);
kunmap(page);
f2fs_put_page(ipage, 1);
out:
SetPageUptodate(page); SetPageUptodate(page);
f2fs_put_page(ipage, 1);
unlock_page(page); unlock_page(page);
return 0; return 0;
} }
static int __f2fs_convert_inline_data(struct inode *inode, struct page *page) int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
{ {
int err = 0;
struct page *ipage;
struct dnode_of_data dn;
void *src_addr, *dst_addr; void *src_addr, *dst_addr;
block_t new_blk_addr; block_t new_blk_addr;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
.type = DATA, .type = DATA,
.rw = WRITE_SYNC | REQ_PRIO, .rw = WRITE_SYNC | REQ_PRIO,
}; };
int dirty, err;
f2fs_bug_on(F2FS_I_SB(dn->inode), page->index);
if (!f2fs_exist_data(dn->inode))
goto clear_out;
err = f2fs_reserve_block(dn, 0);
if (err)
return err;
f2fs_wait_on_page_writeback(page, DATA);
if (PageUptodate(page))
goto no_update;
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
/* Copy the whole inline data block */
src_addr = inline_data_addr(dn->inode_page);
dst_addr = kmap_atomic(page);
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
flush_dcache_page(page);
kunmap_atomic(dst_addr);
SetPageUptodate(page);
no_update:
/* clear dirty state */
dirty = clear_page_dirty_for_io(page);
/* write data page to try to make data consistent */
set_page_writeback(page);
write_data_page(page, dn, &new_blk_addr, &fio);
update_extent_cache(new_blk_addr, dn);
f2fs_wait_on_page_writeback(page, DATA);
if (dirty)
inode_dec_dirty_pages(dn->inode);
/* this converted inline_data should be recovered. */
set_inode_flag(F2FS_I(dn->inode), FI_APPEND_WRITE);
/* clear inline data and flag after data writeback */
truncate_inline_data(dn->inode_page, 0);
clear_out:
stat_dec_inline_inode(dn->inode);
f2fs_clear_inline_inode(dn->inode);
sync_inode_page(dn);
f2fs_put_dnode(dn);
return 0;
}
int f2fs_convert_inline_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct dnode_of_data dn;
struct page *ipage, *page;
int err = 0;
page = grab_cache_page(inode->i_mapping, 0);
if (!page)
return -ENOMEM;
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
ipage = get_node_page(sbi, inode->i_ino); ipage = get_node_page(sbi, inode->i_ino);
if (IS_ERR(ipage)) { if (IS_ERR(ipage)) {
err = PTR_ERR(ipage); err = PTR_ERR(ipage);
goto out; goto out;
} }
/* someone else converted inline_data already */ set_new_dnode(&dn, inode, ipage, ipage, 0);
if (!f2fs_has_inline_data(inode))
goto out;
/* if (f2fs_has_inline_data(inode))
* i_addr[0] is not used for inline data, err = f2fs_convert_inline_page(&dn, page);
* so reserving new block will not destroy inline data
*/
set_new_dnode(&dn, inode, ipage, NULL, 0);
err = f2fs_reserve_block(&dn, 0);
if (err)
goto out;
f2fs_wait_on_page_writeback(page, DATA);
zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE);
/* Copy the whole inline data block */
src_addr = inline_data_addr(ipage);
dst_addr = kmap(page);
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
kunmap(page);
SetPageUptodate(page);
/* write data page to try to make data consistent */
set_page_writeback(page);
write_data_page(page, &dn, &new_blk_addr, &fio);
update_extent_cache(new_blk_addr, &dn);
f2fs_wait_on_page_writeback(page, DATA);
/* clear inline data and flag after data writeback */
zero_user_segment(ipage, INLINE_DATA_OFFSET,
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
stat_dec_inline_inode(inode);
sync_inode_page(&dn);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
out: out:
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
f2fs_put_page(page, 1);
return err; return err;
} }
int f2fs_convert_inline_data(struct inode *inode, pgoff_t to_size, int f2fs_write_inline_data(struct inode *inode, struct page *page)
struct page *page)
{
struct page *new_page = page;
int err;
if (!f2fs_has_inline_data(inode))
return 0;
else if (to_size <= MAX_INLINE_DATA)
return 0;
if (!page || page->index != 0) {
new_page = grab_cache_page(inode->i_mapping, 0);
if (!new_page)
return -ENOMEM;
}
err = __f2fs_convert_inline_data(inode, new_page);
if (!page || page->index != 0)
f2fs_put_page(new_page, 1);
return err;
}
int f2fs_write_inline_data(struct inode *inode,
struct page *page, unsigned size)
{ {
void *src_addr, *dst_addr; void *src_addr, *dst_addr;
struct page *ipage;
struct dnode_of_data dn; struct dnode_of_data dn;
int err; int err;
@ -164,47 +177,39 @@ int f2fs_write_inline_data(struct inode *inode,
err = get_dnode_of_data(&dn, 0, LOOKUP_NODE); err = get_dnode_of_data(&dn, 0, LOOKUP_NODE);
if (err) if (err)
return err; return err;
ipage = dn.inode_page;
f2fs_wait_on_page_writeback(ipage, NODE);
zero_user_segment(ipage, INLINE_DATA_OFFSET,
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
src_addr = kmap(page);
dst_addr = inline_data_addr(ipage);
memcpy(dst_addr, src_addr, size);
kunmap(page);
/* Release the first data block if it is allocated */
if (!f2fs_has_inline_data(inode)) { if (!f2fs_has_inline_data(inode)) {
truncate_data_blocks_range(&dn, 1); f2fs_put_dnode(&dn);
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); return -EAGAIN;
stat_inc_inline_inode(inode);
} }
f2fs_bug_on(F2FS_I_SB(inode), page->index);
f2fs_wait_on_page_writeback(dn.inode_page, NODE);
src_addr = kmap_atomic(page);
dst_addr = inline_data_addr(dn.inode_page);
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
kunmap_atomic(src_addr);
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
sync_inode_page(&dn); sync_inode_page(&dn);
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
return 0; return 0;
} }
void truncate_inline_data(struct inode *inode, u64 from) void truncate_inline_data(struct page *ipage, u64 from)
{ {
struct page *ipage; void *addr;
if (from >= MAX_INLINE_DATA) if (from >= MAX_INLINE_DATA)
return; return;
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
if (IS_ERR(ipage))
return;
f2fs_wait_on_page_writeback(ipage, NODE); f2fs_wait_on_page_writeback(ipage, NODE);
zero_user_segment(ipage, INLINE_DATA_OFFSET + from, addr = inline_data_addr(ipage);
INLINE_DATA_OFFSET + MAX_INLINE_DATA); memset(addr + from, 0, MAX_INLINE_DATA - from);
set_page_dirty(ipage);
f2fs_put_page(ipage, 1);
} }
bool recover_inline_data(struct inode *inode, struct page *npage) bool recover_inline_data(struct inode *inode, struct page *npage)
@ -236,6 +241,10 @@ process_inline:
src_addr = inline_data_addr(npage); src_addr = inline_data_addr(npage);
dst_addr = inline_data_addr(ipage); dst_addr = inline_data_addr(ipage);
memcpy(dst_addr, src_addr, MAX_INLINE_DATA); memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
update_inode(inode, ipage); update_inode(inode, ipage);
f2fs_put_page(ipage, 1); f2fs_put_page(ipage, 1);
return true; return true;
@ -244,16 +253,279 @@ process_inline:
if (f2fs_has_inline_data(inode)) { if (f2fs_has_inline_data(inode)) {
ipage = get_node_page(sbi, inode->i_ino); ipage = get_node_page(sbi, inode->i_ino);
f2fs_bug_on(sbi, IS_ERR(ipage)); f2fs_bug_on(sbi, IS_ERR(ipage));
f2fs_wait_on_page_writeback(ipage, NODE); truncate_inline_data(ipage, 0);
zero_user_segment(ipage, INLINE_DATA_OFFSET, f2fs_clear_inline_inode(inode);
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
update_inode(inode, ipage); update_inode(inode, ipage);
f2fs_put_page(ipage, 1); f2fs_put_page(ipage, 1);
} else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) { } else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) {
truncate_blocks(inode, 0, false); truncate_blocks(inode, 0, false);
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
goto process_inline; goto process_inline;
} }
return false; return false;
} }
struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
struct qstr *name, struct page **res_page)
{
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
struct f2fs_inline_dentry *inline_dentry;
struct f2fs_dir_entry *de;
struct f2fs_dentry_ptr d;
struct page *ipage;
ipage = get_node_page(sbi, dir->i_ino);
if (IS_ERR(ipage))
return NULL;
inline_dentry = inline_data_addr(ipage);
make_dentry_ptr(&d, (void *)inline_dentry, 2);
de = find_target_dentry(name, NULL, &d);
unlock_page(ipage);
if (de)
*res_page = ipage;
else
f2fs_put_page(ipage, 0);
/*
* For the most part, it should be a bug when name_len is zero.
* We stop here for figuring out where the bugs has occurred.
*/
f2fs_bug_on(sbi, d.max < 0);
return de;
}
struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *dir,
struct page **p)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct page *ipage;
struct f2fs_dir_entry *de;
struct f2fs_inline_dentry *dentry_blk;
ipage = get_node_page(sbi, dir->i_ino);
if (IS_ERR(ipage))
return NULL;
dentry_blk = inline_data_addr(ipage);
de = &dentry_blk->dentry[1];
*p = ipage;
unlock_page(ipage);
return de;
}
int make_empty_inline_dir(struct inode *inode, struct inode *parent,
struct page *ipage)
{
struct f2fs_inline_dentry *dentry_blk;
struct f2fs_dentry_ptr d;
dentry_blk = inline_data_addr(ipage);
make_dentry_ptr(&d, (void *)dentry_blk, 2);
do_make_empty_dir(inode, parent, &d);
set_page_dirty(ipage);
/* update i_size to MAX_INLINE_DATA */
if (i_size_read(inode) < MAX_INLINE_DATA) {
i_size_write(inode, MAX_INLINE_DATA);
set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR);
}
return 0;
}
static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage,
struct f2fs_inline_dentry *inline_dentry)
{
struct page *page;
struct dnode_of_data dn;
struct f2fs_dentry_block *dentry_blk;
int err;
page = grab_cache_page(dir->i_mapping, 0);
if (!page)
return -ENOMEM;
set_new_dnode(&dn, dir, ipage, NULL, 0);
err = f2fs_reserve_block(&dn, 0);
if (err)
goto out;
f2fs_wait_on_page_writeback(page, DATA);
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
dentry_blk = kmap_atomic(page);
/* copy data from inline dentry block to new dentry block */
memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap,
INLINE_DENTRY_BITMAP_SIZE);
memcpy(dentry_blk->dentry, inline_dentry->dentry,
sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY);
memcpy(dentry_blk->filename, inline_dentry->filename,
NR_INLINE_DENTRY * F2FS_SLOT_LEN);
kunmap_atomic(dentry_blk);
SetPageUptodate(page);
set_page_dirty(page);
/* clear inline dir and flag after data writeback */
truncate_inline_data(ipage, 0);
stat_dec_inline_dir(dir);
clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY);
if (i_size_read(dir) < PAGE_CACHE_SIZE) {
i_size_write(dir, PAGE_CACHE_SIZE);
set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
}
sync_inode_page(&dn);
out:
f2fs_put_page(page, 1);
return err;
}
int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct page *ipage;
unsigned int bit_pos;
f2fs_hash_t name_hash;
struct f2fs_dir_entry *de;
size_t namelen = name->len;
struct f2fs_inline_dentry *dentry_blk = NULL;
int slots = GET_DENTRY_SLOTS(namelen);
struct page *page;
int err = 0;
int i;
name_hash = f2fs_dentry_hash(name);
ipage = get_node_page(sbi, dir->i_ino);
if (IS_ERR(ipage))
return PTR_ERR(ipage);
dentry_blk = inline_data_addr(ipage);
bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
slots, NR_INLINE_DENTRY);
if (bit_pos >= NR_INLINE_DENTRY) {
err = f2fs_convert_inline_dir(dir, ipage, dentry_blk);
if (!err)
err = -EAGAIN;
goto out;
}
down_write(&F2FS_I(inode)->i_sem);
page = init_inode_metadata(inode, dir, name, ipage);
if (IS_ERR(page)) {
err = PTR_ERR(page);
goto fail;
}
f2fs_wait_on_page_writeback(ipage, NODE);
de = &dentry_blk->dentry[bit_pos];
de->hash_code = name_hash;
de->name_len = cpu_to_le16(namelen);
memcpy(dentry_blk->filename[bit_pos], name->name, name->len);
de->ino = cpu_to_le32(inode->i_ino);
set_de_type(de, inode);
for (i = 0; i < slots; i++)
test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
set_page_dirty(ipage);
/* we don't need to mark_inode_dirty now */
F2FS_I(inode)->i_pino = dir->i_ino;
update_inode(inode, page);
f2fs_put_page(page, 1);
update_parent_metadata(dir, inode, 0);
fail:
up_write(&F2FS_I(inode)->i_sem);
if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) {
update_inode(dir, ipage);
clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
}
out:
f2fs_put_page(ipage, 1);
return err;
}
void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page,
struct inode *dir, struct inode *inode)
{
struct f2fs_inline_dentry *inline_dentry;
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
unsigned int bit_pos;
int i;
lock_page(page);
f2fs_wait_on_page_writeback(page, NODE);
inline_dentry = inline_data_addr(page);
bit_pos = dentry - inline_dentry->dentry;
for (i = 0; i < slots; i++)
test_and_clear_bit_le(bit_pos + i,
&inline_dentry->dentry_bitmap);
set_page_dirty(page);
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
if (inode)
f2fs_drop_nlink(dir, inode, page);
f2fs_put_page(page, 1);
}
bool f2fs_empty_inline_dir(struct inode *dir)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
struct page *ipage;
unsigned int bit_pos = 2;
struct f2fs_inline_dentry *dentry_blk;
ipage = get_node_page(sbi, dir->i_ino);
if (IS_ERR(ipage))
return false;
dentry_blk = inline_data_addr(ipage);
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
NR_INLINE_DENTRY,
bit_pos);
f2fs_put_page(ipage, 1);
if (bit_pos < NR_INLINE_DENTRY)
return false;
return true;
}
int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx)
{
struct inode *inode = file_inode(file);
struct f2fs_inline_dentry *inline_dentry = NULL;
struct page *ipage = NULL;
struct f2fs_dentry_ptr d;
if (ctx->pos == NR_INLINE_DENTRY)
return 0;
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
if (IS_ERR(ipage))
return PTR_ERR(ipage);
inline_dentry = inline_data_addr(ipage);
make_dentry_ptr(&d, (void *)inline_dentry, 2);
if (!f2fs_fill_dentries(ctx, &d, 0))
ctx->pos = NR_INLINE_DENTRY;
f2fs_put_page(ipage, 1);
return 0;
}

View File

@ -67,12 +67,38 @@ static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
} }
} }
static int __recover_inline_status(struct inode *inode, struct page *ipage)
{
void *inline_data = inline_data_addr(ipage);
struct f2fs_inode *ri;
void *zbuf;
zbuf = kzalloc(MAX_INLINE_DATA, GFP_NOFS);
if (!zbuf)
return -ENOMEM;
if (!memcmp(zbuf, inline_data, MAX_INLINE_DATA)) {
kfree(zbuf);
return 0;
}
kfree(zbuf);
f2fs_wait_on_page_writeback(ipage, NODE);
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
ri = F2FS_INODE(ipage);
set_raw_inline(F2FS_I(inode), ri);
set_page_dirty(ipage);
return 0;
}
static int do_read_inode(struct inode *inode) static int do_read_inode(struct inode *inode)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
struct page *node_page; struct page *node_page;
struct f2fs_inode *ri; struct f2fs_inode *ri;
int err = 0;
/* Check if ino is within scope */ /* Check if ino is within scope */
if (check_nid_range(sbi, inode->i_ino)) { if (check_nid_range(sbi, inode->i_ino)) {
@ -114,11 +140,19 @@ static int do_read_inode(struct inode *inode)
get_extent_info(&fi->ext, ri->i_ext); get_extent_info(&fi->ext, ri->i_ext);
get_inline_info(fi, ri); get_inline_info(fi, ri);
/* check data exist */
if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode))
err = __recover_inline_status(inode, node_page);
/* get rdev by using inline_info */ /* get rdev by using inline_info */
__get_inode_rdev(inode, ri); __get_inode_rdev(inode, ri);
f2fs_put_page(node_page, 1); f2fs_put_page(node_page, 1);
return 0;
stat_inc_inline_inode(inode);
stat_inc_inline_dir(inode);
return err;
} }
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
@ -156,7 +190,7 @@ make_now:
inode->i_op = &f2fs_dir_inode_operations; inode->i_op = &f2fs_dir_inode_operations;
inode->i_fop = &f2fs_dir_operations; inode->i_fop = &f2fs_dir_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
} else if (S_ISLNK(inode->i_mode)) { } else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &f2fs_symlink_inode_operations; inode->i_op = &f2fs_symlink_inode_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
@ -295,11 +329,12 @@ void f2fs_evict_inode(struct inode *inode)
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
remove_inode_page(inode); remove_inode_page(inode);
stat_dec_inline_inode(inode);
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
sb_end_intwrite(inode->i_sb); sb_end_intwrite(inode->i_sb);
no_delete: no_delete:
stat_dec_inline_dir(inode);
stat_dec_inline_inode(inode);
invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino); invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino);
if (xnid) if (xnid)
invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid); invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid);
@ -325,8 +360,9 @@ void handle_failed_inode(struct inode *inode)
f2fs_truncate(inode); f2fs_truncate(inode);
remove_inode_page(inode); remove_inode_page(inode);
stat_dec_inline_inode(inode);
clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
clear_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY);
alloc_nid_failed(sbi, inode->i_ino); alloc_nid_failed(sbi, inode->i_ino);
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);

View File

@ -54,6 +54,12 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
nid_free = true; nid_free = true;
goto out; goto out;
} }
if (f2fs_may_inline(inode))
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
if (test_opt(sbi, INLINE_DENTRY) && S_ISDIR(inode->i_mode))
set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY);
trace_f2fs_new_inode(inode, 0); trace_f2fs_new_inode(inode, 0);
mark_inode_dirty(inode); mark_inode_dirty(inode);
return inode; return inode;
@ -129,8 +135,12 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
alloc_nid_done(sbi, ino); alloc_nid_done(sbi, ino);
stat_inc_inline_inode(inode);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
return 0; return 0;
out: out:
handle_failed_inode(inode); handle_failed_inode(inode);
@ -157,6 +167,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
return 0; return 0;
out: out:
clear_inode_flag(F2FS_I(inode), FI_INC_LINK); clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
@ -187,14 +200,12 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
de = f2fs_find_entry(dir, &dentry->d_name, &page); de = f2fs_find_entry(dir, &dentry->d_name, &page);
if (de) { if (de) {
nid_t ino = le32_to_cpu(de->ino); nid_t ino = le32_to_cpu(de->ino);
kunmap(page); f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
inode = f2fs_iget(dir->i_sb, ino); inode = f2fs_iget(dir->i_sb, ino);
if (IS_ERR(inode)) if (IS_ERR(inode))
return ERR_CAST(inode); return ERR_CAST(inode);
stat_inc_inline_inode(inode);
} }
return d_splice_alias(inode, dentry); return d_splice_alias(inode, dentry);
@ -219,15 +230,18 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
err = acquire_orphan_inode(sbi); err = acquire_orphan_inode(sbi);
if (err) { if (err) {
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
kunmap(page); f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
goto fail; goto fail;
} }
f2fs_delete_entry(de, page, inode); f2fs_delete_entry(de, page, dir, inode);
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
/* In order to evict this inode, we set it dirty */ /* In order to evict this inode, we set it dirty */
mark_inode_dirty(inode); mark_inode_dirty(inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
fail: fail:
trace_f2fs_unlink_exit(inode, err); trace_f2fs_unlink_exit(inode, err);
return err; return err;
@ -261,6 +275,9 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
return err; return err;
out: out:
handle_failed_inode(inode); handle_failed_inode(inode);
@ -291,11 +308,14 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
goto out_fail; goto out_fail;
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
stat_inc_inline_dir(inode);
alloc_nid_done(sbi, inode->i_ino); alloc_nid_done(sbi, inode->i_ino);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
return 0; return 0;
out_fail: out_fail:
@ -338,8 +358,12 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
alloc_nid_done(sbi, inode->i_ino); alloc_nid_done(sbi, inode->i_ino);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
if (IS_DIRSYNC(dir))
f2fs_sync_fs(sbi->sb, 1);
return 0; return 0;
out: out:
handle_failed_inode(inode); handle_failed_inode(inode);
@ -435,7 +459,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
old_inode->i_ctime = CURRENT_TIME; old_inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(old_inode); mark_inode_dirty(old_inode);
f2fs_delete_entry(old_entry, old_page, NULL); f2fs_delete_entry(old_entry, old_page, old_dir, NULL);
if (old_dir_entry) { if (old_dir_entry) {
if (old_dir != new_dir) { if (old_dir != new_dir) {
@ -443,7 +467,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
old_dir_page, new_dir); old_dir_page, new_dir);
update_inode_page(old_inode); update_inode_page(old_inode);
} else { } else {
kunmap(old_dir_page); f2fs_dentry_kunmap(old_inode, old_dir_page);
f2fs_put_page(old_dir_page, 0); f2fs_put_page(old_dir_page, 0);
} }
drop_nlink(old_dir); drop_nlink(old_dir);
@ -452,19 +476,22 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
} }
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
f2fs_sync_fs(sbi->sb, 1);
return 0; return 0;
put_out_dir: put_out_dir:
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
kunmap(new_page); f2fs_dentry_kunmap(new_dir, new_page);
f2fs_put_page(new_page, 0); f2fs_put_page(new_page, 0);
out_dir: out_dir:
if (old_dir_entry) { if (old_dir_entry) {
kunmap(old_dir_page); f2fs_dentry_kunmap(old_inode, old_dir_page);
f2fs_put_page(old_dir_page, 0); f2fs_put_page(old_dir_page, 0);
} }
out_old: out_old:
kunmap(old_page); f2fs_dentry_kunmap(old_dir, old_page);
f2fs_put_page(old_page, 0); f2fs_put_page(old_page, 0);
out: out:
return err; return err;
@ -588,6 +615,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
update_inode_page(new_dir); update_inode_page(new_dir);
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
f2fs_sync_fs(sbi->sb, 1);
return 0; return 0;
out_undo: out_undo:
/* Still we may fail to recover name info of f2fs_inode here */ /* Still we may fail to recover name info of f2fs_inode here */
@ -596,19 +626,19 @@ out_unlock:
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
out_new_dir: out_new_dir:
if (new_dir_entry) { if (new_dir_entry) {
kunmap(new_dir_page); f2fs_dentry_kunmap(new_inode, new_dir_page);
f2fs_put_page(new_dir_page, 0); f2fs_put_page(new_dir_page, 0);
} }
out_old_dir: out_old_dir:
if (old_dir_entry) { if (old_dir_entry) {
kunmap(old_dir_page); f2fs_dentry_kunmap(old_inode, old_dir_page);
f2fs_put_page(old_dir_page, 0); f2fs_put_page(old_dir_page, 0);
} }
out_new: out_new:
kunmap(new_page); f2fs_dentry_kunmap(new_dir, new_page);
f2fs_put_page(new_page, 0); f2fs_put_page(new_page, 0);
out_old: out_old:
kunmap(old_page); f2fs_dentry_kunmap(old_dir, old_page);
f2fs_put_page(old_page, 0); f2fs_put_page(old_page, 0);
out: out:
return err; return err;

View File

@ -31,22 +31,38 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type)
{ {
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
struct sysinfo val; struct sysinfo val;
unsigned long avail_ram;
unsigned long mem_size = 0; unsigned long mem_size = 0;
bool res = false; bool res = false;
si_meminfo(&val); si_meminfo(&val);
/* give 25%, 25%, 50% memory for each components respectively */
/* only uses low memory */
avail_ram = val.totalram - val.totalhigh;
/* give 25%, 25%, 50%, 50% memory for each components respectively */
if (type == FREE_NIDS) { if (type == FREE_NIDS) {
mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >> 12; mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >>
res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 2); PAGE_CACHE_SHIFT;
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
} else if (type == NAT_ENTRIES) { } else if (type == NAT_ENTRIES) {
mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> 12; mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >>
res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 2); PAGE_CACHE_SHIFT;
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
} else if (type == DIRTY_DENTS) { } else if (type == DIRTY_DENTS) {
if (sbi->sb->s_bdi->dirty_exceeded) if (sbi->sb->s_bdi->dirty_exceeded)
return false; return false;
mem_size = get_pages(sbi, F2FS_DIRTY_DENTS); mem_size = get_pages(sbi, F2FS_DIRTY_DENTS);
res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 1); res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);
} else if (type == INO_ENTRIES) {
int i;
if (sbi->sb->s_bdi->dirty_exceeded)
return false;
for (i = 0; i <= UPDATE_INO; i++)
mem_size += (sbi->im[i].ino_num *
sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT;
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);
} }
return res; return res;
} }
@ -131,7 +147,7 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i,
if (get_nat_flag(ne, IS_DIRTY)) if (get_nat_flag(ne, IS_DIRTY))
return; return;
retry:
head = radix_tree_lookup(&nm_i->nat_set_root, set); head = radix_tree_lookup(&nm_i->nat_set_root, set);
if (!head) { if (!head) {
head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_ATOMIC); head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_ATOMIC);
@ -140,11 +156,7 @@ retry:
INIT_LIST_HEAD(&head->set_list); INIT_LIST_HEAD(&head->set_list);
head->set = set; head->set = set;
head->entry_cnt = 0; head->entry_cnt = 0;
f2fs_radix_tree_insert(&nm_i->nat_set_root, set, head);
if (radix_tree_insert(&nm_i->nat_set_root, set, head)) {
cond_resched();
goto retry;
}
} }
list_move_tail(&ne->list, &head->entry_list); list_move_tail(&ne->list, &head->entry_list);
nm_i->dirty_nat_cnt++; nm_i->dirty_nat_cnt++;
@ -155,7 +167,7 @@ retry:
static void __clear_nat_cache_dirty(struct f2fs_nm_info *nm_i, static void __clear_nat_cache_dirty(struct f2fs_nm_info *nm_i,
struct nat_entry *ne) struct nat_entry *ne)
{ {
nid_t set = ne->ni.nid / NAT_ENTRY_PER_BLOCK; nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid);
struct nat_entry_set *head; struct nat_entry_set *head;
head = radix_tree_lookup(&nm_i->nat_set_root, set); head = radix_tree_lookup(&nm_i->nat_set_root, set);
@ -180,11 +192,11 @@ bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid)
struct nat_entry *e; struct nat_entry *e;
bool is_cp = true; bool is_cp = true;
read_lock(&nm_i->nat_tree_lock); down_read(&nm_i->nat_tree_lock);
e = __lookup_nat_cache(nm_i, nid); e = __lookup_nat_cache(nm_i, nid);
if (e && !get_nat_flag(e, IS_CHECKPOINTED)) if (e && !get_nat_flag(e, IS_CHECKPOINTED))
is_cp = false; is_cp = false;
read_unlock(&nm_i->nat_tree_lock); up_read(&nm_i->nat_tree_lock);
return is_cp; return is_cp;
} }
@ -194,11 +206,11 @@ bool has_fsynced_inode(struct f2fs_sb_info *sbi, nid_t ino)
struct nat_entry *e; struct nat_entry *e;
bool fsynced = false; bool fsynced = false;
read_lock(&nm_i->nat_tree_lock); down_read(&nm_i->nat_tree_lock);
e = __lookup_nat_cache(nm_i, ino); e = __lookup_nat_cache(nm_i, ino);
if (e && get_nat_flag(e, HAS_FSYNCED_INODE)) if (e && get_nat_flag(e, HAS_FSYNCED_INODE))
fsynced = true; fsynced = true;
read_unlock(&nm_i->nat_tree_lock); up_read(&nm_i->nat_tree_lock);
return fsynced; return fsynced;
} }
@ -208,13 +220,13 @@ bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino)
struct nat_entry *e; struct nat_entry *e;
bool need_update = true; bool need_update = true;
read_lock(&nm_i->nat_tree_lock); down_read(&nm_i->nat_tree_lock);
e = __lookup_nat_cache(nm_i, ino); e = __lookup_nat_cache(nm_i, ino);
if (e && get_nat_flag(e, HAS_LAST_FSYNC) && if (e && get_nat_flag(e, HAS_LAST_FSYNC) &&
(get_nat_flag(e, IS_CHECKPOINTED) || (get_nat_flag(e, IS_CHECKPOINTED) ||
get_nat_flag(e, HAS_FSYNCED_INODE))) get_nat_flag(e, HAS_FSYNCED_INODE)))
need_update = false; need_update = false;
read_unlock(&nm_i->nat_tree_lock); up_read(&nm_i->nat_tree_lock);
return need_update; return need_update;
} }
@ -222,13 +234,8 @@ static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid)
{ {
struct nat_entry *new; struct nat_entry *new;
new = kmem_cache_alloc(nat_entry_slab, GFP_ATOMIC); new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_ATOMIC);
if (!new) f2fs_radix_tree_insert(&nm_i->nat_root, nid, new);
return NULL;
if (radix_tree_insert(&nm_i->nat_root, nid, new)) {
kmem_cache_free(nat_entry_slab, new);
return NULL;
}
memset(new, 0, sizeof(struct nat_entry)); memset(new, 0, sizeof(struct nat_entry));
nat_set_nid(new, nid); nat_set_nid(new, nid);
nat_reset_flag(new); nat_reset_flag(new);
@ -241,18 +248,14 @@ static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid,
struct f2fs_nat_entry *ne) struct f2fs_nat_entry *ne)
{ {
struct nat_entry *e; struct nat_entry *e;
retry:
write_lock(&nm_i->nat_tree_lock); down_write(&nm_i->nat_tree_lock);
e = __lookup_nat_cache(nm_i, nid); e = __lookup_nat_cache(nm_i, nid);
if (!e) { if (!e) {
e = grab_nat_entry(nm_i, nid); e = grab_nat_entry(nm_i, nid);
if (!e) {
write_unlock(&nm_i->nat_tree_lock);
goto retry;
}
node_info_from_raw_nat(&e->ni, ne); node_info_from_raw_nat(&e->ni, ne);
} }
write_unlock(&nm_i->nat_tree_lock); up_write(&nm_i->nat_tree_lock);
} }
static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
@ -260,15 +263,11 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
{ {
struct f2fs_nm_info *nm_i = NM_I(sbi); struct f2fs_nm_info *nm_i = NM_I(sbi);
struct nat_entry *e; struct nat_entry *e;
retry:
write_lock(&nm_i->nat_tree_lock); down_write(&nm_i->nat_tree_lock);
e = __lookup_nat_cache(nm_i, ni->nid); e = __lookup_nat_cache(nm_i, ni->nid);
if (!e) { if (!e) {
e = grab_nat_entry(nm_i, ni->nid); e = grab_nat_entry(nm_i, ni->nid);
if (!e) {
write_unlock(&nm_i->nat_tree_lock);
goto retry;
}
e->ni = *ni; e->ni = *ni;
f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR); f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR);
} else if (new_blkaddr == NEW_ADDR) { } else if (new_blkaddr == NEW_ADDR) {
@ -310,7 +309,7 @@ retry:
set_nat_flag(e, HAS_FSYNCED_INODE, true); set_nat_flag(e, HAS_FSYNCED_INODE, true);
set_nat_flag(e, HAS_LAST_FSYNC, fsync_done); set_nat_flag(e, HAS_LAST_FSYNC, fsync_done);
} }
write_unlock(&nm_i->nat_tree_lock); up_write(&nm_i->nat_tree_lock);
} }
int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink) int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
@ -320,7 +319,7 @@ int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
if (available_free_memory(sbi, NAT_ENTRIES)) if (available_free_memory(sbi, NAT_ENTRIES))
return 0; return 0;
write_lock(&nm_i->nat_tree_lock); down_write(&nm_i->nat_tree_lock);
while (nr_shrink && !list_empty(&nm_i->nat_entries)) { while (nr_shrink && !list_empty(&nm_i->nat_entries)) {
struct nat_entry *ne; struct nat_entry *ne;
ne = list_first_entry(&nm_i->nat_entries, ne = list_first_entry(&nm_i->nat_entries,
@ -328,7 +327,7 @@ int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
__del_from_nat_cache(nm_i, ne); __del_from_nat_cache(nm_i, ne);
nr_shrink--; nr_shrink--;
} }
write_unlock(&nm_i->nat_tree_lock); up_write(&nm_i->nat_tree_lock);
return nr_shrink; return nr_shrink;
} }
@ -351,14 +350,14 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
ni->nid = nid; ni->nid = nid;
/* Check nat cache */ /* Check nat cache */
read_lock(&nm_i->nat_tree_lock); down_read(&nm_i->nat_tree_lock);
e = __lookup_nat_cache(nm_i, nid); e = __lookup_nat_cache(nm_i, nid);
if (e) { if (e) {
ni->ino = nat_get_ino(e); ni->ino = nat_get_ino(e);
ni->blk_addr = nat_get_blkaddr(e); ni->blk_addr = nat_get_blkaddr(e);
ni->version = nat_get_version(e); ni->version = nat_get_version(e);
} }
read_unlock(&nm_i->nat_tree_lock); up_read(&nm_i->nat_tree_lock);
if (e) if (e)
return; return;
@ -1298,16 +1297,22 @@ static int f2fs_write_node_page(struct page *page,
return 0; return 0;
} }
if (wbc->for_reclaim) if (wbc->for_reclaim) {
goto redirty_out; if (!down_read_trylock(&sbi->node_write))
goto redirty_out;
down_read(&sbi->node_write); } else {
down_read(&sbi->node_write);
}
set_page_writeback(page); set_page_writeback(page);
write_node_page(sbi, page, &fio, nid, ni.blk_addr, &new_addr); write_node_page(sbi, page, &fio, nid, ni.blk_addr, &new_addr);
set_node_addr(sbi, &ni, new_addr, is_fsync_dnode(page)); set_node_addr(sbi, &ni, new_addr, is_fsync_dnode(page));
dec_page_count(sbi, F2FS_DIRTY_NODES); dec_page_count(sbi, F2FS_DIRTY_NODES);
up_read(&sbi->node_write); up_read(&sbi->node_write);
unlock_page(page); unlock_page(page);
if (wbc->for_reclaim)
f2fs_submit_merged_bio(sbi, NODE, WRITE);
return 0; return 0;
redirty_out: redirty_out:
@ -1410,13 +1415,13 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build)
if (build) { if (build) {
/* do not add allocated nids */ /* do not add allocated nids */
read_lock(&nm_i->nat_tree_lock); down_read(&nm_i->nat_tree_lock);
ne = __lookup_nat_cache(nm_i, nid); ne = __lookup_nat_cache(nm_i, nid);
if (ne && if (ne &&
(!get_nat_flag(ne, IS_CHECKPOINTED) || (!get_nat_flag(ne, IS_CHECKPOINTED) ||
nat_get_blkaddr(ne) != NULL_ADDR)) nat_get_blkaddr(ne) != NULL_ADDR))
allocated = true; allocated = true;
read_unlock(&nm_i->nat_tree_lock); up_read(&nm_i->nat_tree_lock);
if (allocated) if (allocated)
return 0; return 0;
} }
@ -1425,15 +1430,22 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build)
i->nid = nid; i->nid = nid;
i->state = NID_NEW; i->state = NID_NEW;
if (radix_tree_preload(GFP_NOFS)) {
kmem_cache_free(free_nid_slab, i);
return 0;
}
spin_lock(&nm_i->free_nid_list_lock); spin_lock(&nm_i->free_nid_list_lock);
if (radix_tree_insert(&nm_i->free_nid_root, i->nid, i)) { if (radix_tree_insert(&nm_i->free_nid_root, i->nid, i)) {
spin_unlock(&nm_i->free_nid_list_lock); spin_unlock(&nm_i->free_nid_list_lock);
radix_tree_preload_end();
kmem_cache_free(free_nid_slab, i); kmem_cache_free(free_nid_slab, i);
return 0; return 0;
} }
list_add_tail(&i->list, &nm_i->free_nid_list); list_add_tail(&i->list, &nm_i->free_nid_list);
nm_i->fcnt++; nm_i->fcnt++;
spin_unlock(&nm_i->free_nid_list_lock); spin_unlock(&nm_i->free_nid_list_lock);
radix_tree_preload_end();
return 1; return 1;
} }
@ -1804,21 +1816,15 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi)
nid_t nid = le32_to_cpu(nid_in_journal(sum, i)); nid_t nid = le32_to_cpu(nid_in_journal(sum, i));
raw_ne = nat_in_journal(sum, i); raw_ne = nat_in_journal(sum, i);
retry:
write_lock(&nm_i->nat_tree_lock);
ne = __lookup_nat_cache(nm_i, nid);
if (ne)
goto found;
ne = grab_nat_entry(nm_i, nid); down_write(&nm_i->nat_tree_lock);
ne = __lookup_nat_cache(nm_i, nid);
if (!ne) { if (!ne) {
write_unlock(&nm_i->nat_tree_lock); ne = grab_nat_entry(nm_i, nid);
goto retry; node_info_from_raw_nat(&ne->ni, &raw_ne);
} }
node_info_from_raw_nat(&ne->ni, &raw_ne);
found:
__set_nat_cache_dirty(nm_i, ne); __set_nat_cache_dirty(nm_i, ne);
write_unlock(&nm_i->nat_tree_lock); up_write(&nm_i->nat_tree_lock);
} }
update_nats_in_cursum(sum, -i); update_nats_in_cursum(sum, -i);
mutex_unlock(&curseg->curseg_mutex); mutex_unlock(&curseg->curseg_mutex);
@ -1889,10 +1895,10 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
} }
raw_nat_from_node_info(raw_ne, &ne->ni); raw_nat_from_node_info(raw_ne, &ne->ni);
write_lock(&NM_I(sbi)->nat_tree_lock); down_write(&NM_I(sbi)->nat_tree_lock);
nat_reset_flag(ne); nat_reset_flag(ne);
__clear_nat_cache_dirty(NM_I(sbi), ne); __clear_nat_cache_dirty(NM_I(sbi), ne);
write_unlock(&NM_I(sbi)->nat_tree_lock); up_write(&NM_I(sbi)->nat_tree_lock);
if (nat_get_blkaddr(ne) == NULL_ADDR) if (nat_get_blkaddr(ne) == NULL_ADDR)
add_free_nid(sbi, nid, false); add_free_nid(sbi, nid, false);
@ -1903,10 +1909,10 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
else else
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
if (!set->entry_cnt) { f2fs_bug_on(sbi, set->entry_cnt);
radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set);
kmem_cache_free(nat_entry_set_slab, set); radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set);
} kmem_cache_free(nat_entry_set_slab, set);
} }
/* /*
@ -1923,6 +1929,8 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
nid_t set_idx = 0; nid_t set_idx = 0;
LIST_HEAD(sets); LIST_HEAD(sets);
if (!nm_i->dirty_nat_cnt)
return;
/* /*
* if there are no enough space in journal to store dirty nat * if there are no enough space in journal to store dirty nat
* entries, remove all entries from journal and merge them * entries, remove all entries from journal and merge them
@ -1931,9 +1939,6 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL)) if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL))
remove_nats_in_journal(sbi); remove_nats_in_journal(sbi);
if (!nm_i->dirty_nat_cnt)
return;
while ((found = __gang_lookup_nat_set(nm_i, while ((found = __gang_lookup_nat_set(nm_i,
set_idx, NATVEC_SIZE, setvec))) { set_idx, NATVEC_SIZE, setvec))) {
unsigned idx; unsigned idx;
@ -1973,13 +1978,13 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC);
INIT_LIST_HEAD(&nm_i->free_nid_list); INIT_LIST_HEAD(&nm_i->free_nid_list);
INIT_RADIX_TREE(&nm_i->nat_root, GFP_ATOMIC); INIT_RADIX_TREE(&nm_i->nat_root, GFP_NOIO);
INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_ATOMIC); INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_NOIO);
INIT_LIST_HEAD(&nm_i->nat_entries); INIT_LIST_HEAD(&nm_i->nat_entries);
mutex_init(&nm_i->build_lock); mutex_init(&nm_i->build_lock);
spin_lock_init(&nm_i->free_nid_list_lock); spin_lock_init(&nm_i->free_nid_list_lock);
rwlock_init(&nm_i->nat_tree_lock); init_rwsem(&nm_i->nat_tree_lock);
nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid); nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP); nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
@ -2035,7 +2040,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi)
spin_unlock(&nm_i->free_nid_list_lock); spin_unlock(&nm_i->free_nid_list_lock);
/* destroy nat cache */ /* destroy nat cache */
write_lock(&nm_i->nat_tree_lock); down_write(&nm_i->nat_tree_lock);
while ((found = __gang_lookup_nat_cache(nm_i, while ((found = __gang_lookup_nat_cache(nm_i,
nid, NATVEC_SIZE, natvec))) { nid, NATVEC_SIZE, natvec))) {
unsigned idx; unsigned idx;
@ -2044,7 +2049,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi)
__del_from_nat_cache(nm_i, natvec[idx]); __del_from_nat_cache(nm_i, natvec[idx]);
} }
f2fs_bug_on(sbi, nm_i->nat_cnt); f2fs_bug_on(sbi, nm_i->nat_cnt);
write_unlock(&nm_i->nat_tree_lock); up_write(&nm_i->nat_tree_lock);
kfree(nm_i->nat_bitmap); kfree(nm_i->nat_bitmap);
sbi->nm_info = NULL; sbi->nm_info = NULL;
@ -2061,17 +2066,17 @@ int __init create_node_manager_caches(void)
free_nid_slab = f2fs_kmem_cache_create("free_nid", free_nid_slab = f2fs_kmem_cache_create("free_nid",
sizeof(struct free_nid)); sizeof(struct free_nid));
if (!free_nid_slab) if (!free_nid_slab)
goto destory_nat_entry; goto destroy_nat_entry;
nat_entry_set_slab = f2fs_kmem_cache_create("nat_entry_set", nat_entry_set_slab = f2fs_kmem_cache_create("nat_entry_set",
sizeof(struct nat_entry_set)); sizeof(struct nat_entry_set));
if (!nat_entry_set_slab) if (!nat_entry_set_slab)
goto destory_free_nid; goto destroy_free_nid;
return 0; return 0;
destory_free_nid: destroy_free_nid:
kmem_cache_destroy(free_nid_slab); kmem_cache_destroy(free_nid_slab);
destory_nat_entry: destroy_nat_entry:
kmem_cache_destroy(nat_entry_slab); kmem_cache_destroy(nat_entry_slab);
fail: fail:
return -ENOMEM; return -ENOMEM;

View File

@ -106,7 +106,8 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne,
enum mem_type { enum mem_type {
FREE_NIDS, /* indicates the free nid list */ FREE_NIDS, /* indicates the free nid list */
NAT_ENTRIES, /* indicates the cached nat entry */ NAT_ENTRIES, /* indicates the cached nat entry */
DIRTY_DENTS /* indicates dirty dentry pages */ DIRTY_DENTS, /* indicates dirty dentry pages */
INO_ENTRIES, /* indicates inode entries */
}; };
struct nat_entry_set { struct nat_entry_set {
@ -192,10 +193,7 @@ static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid)
{ {
unsigned int block_off = NAT_BLOCK_OFFSET(start_nid); unsigned int block_off = NAT_BLOCK_OFFSET(start_nid);
if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) f2fs_change_bit(block_off, nm_i->nat_bitmap);
f2fs_clear_bit(block_off, nm_i->nat_bitmap);
else
f2fs_set_bit(block_off, nm_i->nat_bitmap);
} }
static inline void fill_node_footer(struct page *page, nid_t nid, static inline void fill_node_footer(struct page *page, nid_t nid,

View File

@ -111,7 +111,7 @@ retry:
iput(einode); iput(einode);
goto out_unmap_put; goto out_unmap_put;
} }
f2fs_delete_entry(de, page, einode); f2fs_delete_entry(de, page, dir, einode);
iput(einode); iput(einode);
goto retry; goto retry;
} }
@ -129,7 +129,7 @@ retry:
goto out; goto out;
out_unmap_put: out_unmap_put:
kunmap(page); f2fs_dentry_kunmap(dir, page);
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
out_err: out_err:
iput(dir); iput(dir);
@ -170,13 +170,15 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
ra_meta_pages(sbi, blkaddr, 1, META_POR);
while (1) { while (1) {
struct fsync_inode_entry *entry; struct fsync_inode_entry *entry;
if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi)) if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi))
return 0; return 0;
page = get_meta_page_ra(sbi, blkaddr); page = get_meta_page(sbi, blkaddr);
if (cp_ver != cpver_of_node(page)) if (cp_ver != cpver_of_node(page))
break; break;
@ -227,6 +229,8 @@ next:
/* check next segment */ /* check next segment */
blkaddr = next_blkaddr_of_node(page); blkaddr = next_blkaddr_of_node(page);
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
ra_meta_pages_cond(sbi, blkaddr);
} }
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
return err; return err;
@ -436,7 +440,9 @@ static int recover_data(struct f2fs_sb_info *sbi,
if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi)) if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi))
break; break;
page = get_meta_page_ra(sbi, blkaddr); ra_meta_pages_cond(sbi, blkaddr);
page = get_meta_page(sbi, blkaddr);
if (cp_ver != cpver_of_node(page)) { if (cp_ver != cpver_of_node(page)) {
f2fs_put_page(page, 1); f2fs_put_page(page, 1);

View File

@ -178,17 +178,47 @@ void register_inmem_page(struct inode *inode, struct page *page)
{ {
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
struct inmem_pages *new; struct inmem_pages *new;
int err;
SetPagePrivate(page);
new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS); new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS);
/* add atomic page indices to the list */ /* add atomic page indices to the list */
new->page = page; new->page = page;
INIT_LIST_HEAD(&new->list); INIT_LIST_HEAD(&new->list);
retry:
/* increase reference count with clean state */ /* increase reference count with clean state */
mutex_lock(&fi->inmem_lock); mutex_lock(&fi->inmem_lock);
err = radix_tree_insert(&fi->inmem_root, page->index, new);
if (err == -EEXIST) {
mutex_unlock(&fi->inmem_lock);
kmem_cache_free(inmem_entry_slab, new);
return;
} else if (err) {
mutex_unlock(&fi->inmem_lock);
goto retry;
}
get_page(page); get_page(page);
list_add_tail(&new->list, &fi->inmem_pages); list_add_tail(&new->list, &fi->inmem_pages);
inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
mutex_unlock(&fi->inmem_lock);
}
void invalidate_inmem_page(struct inode *inode, struct page *page)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
struct inmem_pages *cur;
mutex_lock(&fi->inmem_lock);
cur = radix_tree_lookup(&fi->inmem_root, page->index);
if (cur) {
radix_tree_delete(&fi->inmem_root, cur->page->index);
f2fs_put_page(cur->page, 0);
list_del(&cur->list);
kmem_cache_free(inmem_entry_slab, cur);
dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
}
mutex_unlock(&fi->inmem_lock); mutex_unlock(&fi->inmem_lock);
} }
@ -203,7 +233,16 @@ void commit_inmem_pages(struct inode *inode, bool abort)
.rw = WRITE_SYNC, .rw = WRITE_SYNC,
}; };
f2fs_balance_fs(sbi); /*
* The abort is true only when f2fs_evict_inode is called.
* Basically, the f2fs_evict_inode doesn't produce any data writes, so
* that we don't need to call f2fs_balance_fs.
* Otherwise, f2fs_gc in f2fs_balance_fs can wait forever until this
* inode becomes free by iget_locked in f2fs_iget.
*/
if (!abort)
f2fs_balance_fs(sbi);
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
mutex_lock(&fi->inmem_lock); mutex_lock(&fi->inmem_lock);
@ -216,9 +255,11 @@ void commit_inmem_pages(struct inode *inode, bool abort)
do_write_data_page(cur->page, &fio); do_write_data_page(cur->page, &fio);
submit_bio = true; submit_bio = true;
} }
radix_tree_delete(&fi->inmem_root, cur->page->index);
f2fs_put_page(cur->page, 1); f2fs_put_page(cur->page, 1);
list_del(&cur->list); list_del(&cur->list);
kmem_cache_free(inmem_entry_slab, cur); kmem_cache_free(inmem_entry_slab, cur);
dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
} }
if (submit_bio) if (submit_bio)
f2fs_submit_merged_bio(sbi, DATA, WRITE); f2fs_submit_merged_bio(sbi, DATA, WRITE);
@ -248,7 +289,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
{ {
/* check the # of cached NAT entries and prefree segments */ /* check the # of cached NAT entries and prefree segments */
if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK) || if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK) ||
excess_prefree_segs(sbi)) excess_prefree_segs(sbi) ||
available_free_memory(sbi, INO_ENTRIES))
f2fs_sync_fs(sbi->sb, true); f2fs_sync_fs(sbi->sb, true);
} }
@ -441,10 +483,33 @@ void discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr)
} }
} }
static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) static void __add_discard_entry(struct f2fs_sb_info *sbi,
struct cp_control *cpc, unsigned int start, unsigned int end)
{ {
struct list_head *head = &SM_I(sbi)->discard_list; struct list_head *head = &SM_I(sbi)->discard_list;
struct discard_entry *new; struct discard_entry *new, *last;
if (!list_empty(head)) {
last = list_last_entry(head, struct discard_entry, list);
if (START_BLOCK(sbi, cpc->trim_start) + start ==
last->blkaddr + last->len) {
last->len += end - start;
goto done;
}
}
new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS);
INIT_LIST_HEAD(&new->list);
new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start;
new->len = end - start;
list_add_tail(&new->list, head);
done:
SM_I(sbi)->nr_discards += end - start;
cpc->trimmed += end - start;
}
static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)
{
int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long);
int max_blocks = sbi->blocks_per_seg; int max_blocks = sbi->blocks_per_seg;
struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start);
@ -473,13 +538,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)
} }
mutex_unlock(&dirty_i->seglist_lock); mutex_unlock(&dirty_i->seglist_lock);
new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); __add_discard_entry(sbi, cpc, 0, sbi->blocks_per_seg);
INIT_LIST_HEAD(&new->list);
new->blkaddr = START_BLOCK(sbi, cpc->trim_start);
new->len = sbi->blocks_per_seg;
list_add_tail(&new->list, head);
SM_I(sbi)->nr_discards += sbi->blocks_per_seg;
cpc->trimmed += sbi->blocks_per_seg;
return; return;
} }
@ -489,7 +548,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)
/* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */
for (i = 0; i < entries; i++) for (i = 0; i < entries; i++)
dmap[i] = (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; dmap[i] = ~(cur_map[i] | ckpt_map[i]);
while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) { while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) {
start = __find_rev_next_bit(dmap, max_blocks, end + 1); start = __find_rev_next_bit(dmap, max_blocks, end + 1);
@ -501,14 +560,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)
if (end - start < cpc->trim_minlen) if (end - start < cpc->trim_minlen)
continue; continue;
new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); __add_discard_entry(sbi, cpc, start, end);
INIT_LIST_HEAD(&new->list);
new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start;
new->len = end - start;
cpc->trimmed += end - start;
list_add_tail(&new->list, head);
SM_I(sbi)->nr_discards += end - start;
} }
} }
@ -620,10 +672,10 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
/* Update valid block bitmap */ /* Update valid block bitmap */
if (del > 0) { if (del > 0) {
if (f2fs_set_bit(offset, se->cur_valid_map)) if (f2fs_test_and_set_bit(offset, se->cur_valid_map))
f2fs_bug_on(sbi, 1); f2fs_bug_on(sbi, 1);
} else { } else {
if (!f2fs_clear_bit(offset, se->cur_valid_map)) if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map))
f2fs_bug_on(sbi, 1); f2fs_bug_on(sbi, 1);
} }
if (!f2fs_test_bit(offset, se->ckpt_valid_map)) if (!f2fs_test_bit(offset, se->ckpt_valid_map))
@ -1004,6 +1056,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
range->len < sbi->blocksize) range->len < sbi->blocksize)
return -EINVAL; return -EINVAL;
cpc.trimmed = 0;
if (end <= MAIN_BLKADDR(sbi)) if (end <= MAIN_BLKADDR(sbi))
goto out; goto out;
@ -1015,10 +1068,11 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
cpc.trim_start = start_segno; cpc.trim_start = start_segno;
cpc.trim_end = end_segno; cpc.trim_end = end_segno;
cpc.trim_minlen = range->minlen >> sbi->log_blocksize; cpc.trim_minlen = range->minlen >> sbi->log_blocksize;
cpc.trimmed = 0;
/* do checkpoint to issue discard commands safely */ /* do checkpoint to issue discard commands safely */
mutex_lock(&sbi->gc_mutex);
write_checkpoint(sbi, &cpc); write_checkpoint(sbi, &cpc);
mutex_unlock(&sbi->gc_mutex);
out: out:
range->len = cpc.trimmed << sbi->log_blocksize; range->len = cpc.trimmed << sbi->log_blocksize;
return 0; return 0;
@ -1050,8 +1104,8 @@ static int __get_segment_type_4(struct page *page, enum page_type p_type)
else else
return CURSEG_COLD_DATA; return CURSEG_COLD_DATA;
} else { } else {
if (IS_DNODE(page) && !is_cold_node(page)) if (IS_DNODE(page) && is_cold_node(page))
return CURSEG_HOT_NODE; return CURSEG_WARM_NODE;
else else
return CURSEG_COLD_NODE; return CURSEG_COLD_NODE;
} }
@ -1524,17 +1578,7 @@ int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type,
static struct page *get_current_sit_page(struct f2fs_sb_info *sbi, static struct page *get_current_sit_page(struct f2fs_sb_info *sbi,
unsigned int segno) unsigned int segno)
{ {
struct sit_info *sit_i = SIT_I(sbi); return get_meta_page(sbi, current_sit_addr(sbi, segno));
unsigned int offset = SIT_BLOCK_OFFSET(segno);
block_t blk_addr = sit_i->sit_base_addr + offset;
check_seg_range(sbi, segno);
/* calculate sit block address */
if (f2fs_test_bit(offset, sit_i->sit_bitmap))
blk_addr += sit_i->sit_blocks;
return get_meta_page(sbi, blk_addr);
} }
static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, static struct page *get_next_sit_page(struct f2fs_sb_info *sbi,
@ -1687,7 +1731,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
* #2, flush sit entries to sit page. * #2, flush sit entries to sit page.
*/ */
list_for_each_entry_safe(ses, tmp, head, set_list) { list_for_each_entry_safe(ses, tmp, head, set_list) {
struct page *page; struct page *page = NULL;
struct f2fs_sit_block *raw_sit = NULL; struct f2fs_sit_block *raw_sit = NULL;
unsigned int start_segno = ses->start_segno; unsigned int start_segno = ses->start_segno;
unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK, unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK,
@ -2200,7 +2244,7 @@ int __init create_segment_manager_caches(void)
goto fail; goto fail;
sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set", sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set",
sizeof(struct nat_entry_set)); sizeof(struct sit_entry_set));
if (!sit_entry_set_slab) if (!sit_entry_set_slab)
goto destory_discard_entry; goto destory_discard_entry;

View File

@ -657,10 +657,7 @@ static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start)
{ {
unsigned int block_off = SIT_BLOCK_OFFSET(start); unsigned int block_off = SIT_BLOCK_OFFSET(start);
if (f2fs_test_bit(block_off, sit_i->sit_bitmap)) f2fs_change_bit(block_off, sit_i->sit_bitmap);
f2fs_clear_bit(block_off, sit_i->sit_bitmap);
else
f2fs_set_bit(block_off, sit_i->sit_bitmap);
} }
static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi) static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi)
@ -714,6 +711,9 @@ static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi)
*/ */
static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type) static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type)
{ {
if (sbi->sb->s_bdi->dirty_exceeded)
return 0;
if (type == DATA) if (type == DATA)
return sbi->blocks_per_seg; return sbi->blocks_per_seg;
else if (type == NODE) else if (type == NODE)

View File

@ -51,8 +51,10 @@ enum {
Opt_disable_ext_identify, Opt_disable_ext_identify,
Opt_inline_xattr, Opt_inline_xattr,
Opt_inline_data, Opt_inline_data,
Opt_inline_dentry,
Opt_flush_merge, Opt_flush_merge,
Opt_nobarrier, Opt_nobarrier,
Opt_fastboot,
Opt_err, Opt_err,
}; };
@ -69,8 +71,10 @@ static match_table_t f2fs_tokens = {
{Opt_disable_ext_identify, "disable_ext_identify"}, {Opt_disable_ext_identify, "disable_ext_identify"},
{Opt_inline_xattr, "inline_xattr"}, {Opt_inline_xattr, "inline_xattr"},
{Opt_inline_data, "inline_data"}, {Opt_inline_data, "inline_data"},
{Opt_inline_dentry, "inline_dentry"},
{Opt_flush_merge, "flush_merge"}, {Opt_flush_merge, "flush_merge"},
{Opt_nobarrier, "nobarrier"}, {Opt_nobarrier, "nobarrier"},
{Opt_fastboot, "fastboot"},
{Opt_err, NULL}, {Opt_err, NULL},
}; };
@ -340,12 +344,18 @@ static int parse_options(struct super_block *sb, char *options)
case Opt_inline_data: case Opt_inline_data:
set_opt(sbi, INLINE_DATA); set_opt(sbi, INLINE_DATA);
break; break;
case Opt_inline_dentry:
set_opt(sbi, INLINE_DENTRY);
break;
case Opt_flush_merge: case Opt_flush_merge:
set_opt(sbi, FLUSH_MERGE); set_opt(sbi, FLUSH_MERGE);
break; break;
case Opt_nobarrier: case Opt_nobarrier:
set_opt(sbi, NOBARRIER); set_opt(sbi, NOBARRIER);
break; break;
case Opt_fastboot:
set_opt(sbi, FASTBOOT);
break;
default: default:
f2fs_msg(sb, KERN_ERR, f2fs_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" or missing value", "Unrecognized mount option \"%s\" or missing value",
@ -373,6 +383,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
fi->i_advise = 0; fi->i_advise = 0;
rwlock_init(&fi->ext.ext_lock); rwlock_init(&fi->ext.ext_lock);
init_rwsem(&fi->i_sem); init_rwsem(&fi->i_sem);
INIT_RADIX_TREE(&fi->inmem_root, GFP_NOFS);
INIT_LIST_HEAD(&fi->inmem_pages); INIT_LIST_HEAD(&fi->inmem_pages);
mutex_init(&fi->inmem_lock); mutex_init(&fi->inmem_lock);
@ -473,9 +484,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
trace_f2fs_sync_fs(sb, sync); trace_f2fs_sync_fs(sb, sync);
if (sync) { if (sync) {
struct cp_control cpc = { struct cp_control cpc;
.reason = CP_SYNC,
}; cpc.reason = test_opt(sbi, FASTBOOT) ? CP_UMOUNT : CP_SYNC;
mutex_lock(&sbi->gc_mutex); mutex_lock(&sbi->gc_mutex);
write_checkpoint(sbi, &cpc); write_checkpoint(sbi, &cpc);
mutex_unlock(&sbi->gc_mutex); mutex_unlock(&sbi->gc_mutex);
@ -562,10 +573,14 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_puts(seq, ",disable_ext_identify"); seq_puts(seq, ",disable_ext_identify");
if (test_opt(sbi, INLINE_DATA)) if (test_opt(sbi, INLINE_DATA))
seq_puts(seq, ",inline_data"); seq_puts(seq, ",inline_data");
if (test_opt(sbi, INLINE_DENTRY))
seq_puts(seq, ",inline_dentry");
if (!f2fs_readonly(sbi->sb) && test_opt(sbi, FLUSH_MERGE)) if (!f2fs_readonly(sbi->sb) && test_opt(sbi, FLUSH_MERGE))
seq_puts(seq, ",flush_merge"); seq_puts(seq, ",flush_merge");
if (test_opt(sbi, NOBARRIER)) if (test_opt(sbi, NOBARRIER))
seq_puts(seq, ",nobarrier"); seq_puts(seq, ",nobarrier");
if (test_opt(sbi, FASTBOOT))
seq_puts(seq, ",fastboot");
seq_printf(seq, ",active_logs=%u", sbi->active_logs); seq_printf(seq, ",active_logs=%u", sbi->active_logs);
return 0; return 0;
@ -654,7 +669,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
f2fs_sync_fs(sb, 1); f2fs_sync_fs(sb, 1);
need_restart_gc = true; need_restart_gc = true;
} }
} else if (test_opt(sbi, BG_GC) && !sbi->gc_thread) { } else if (!sbi->gc_thread) {
err = start_gc_thread(sbi); err = start_gc_thread(sbi);
if (err) if (err)
goto restore_opts; goto restore_opts;
@ -667,7 +682,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
*/ */
if ((*flags & MS_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) { if ((*flags & MS_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) {
destroy_flush_cmd_control(sbi); destroy_flush_cmd_control(sbi);
} else if (test_opt(sbi, FLUSH_MERGE) && !SM_I(sbi)->cmd_control_info) { } else if (!SM_I(sbi)->cmd_control_info) {
err = create_flush_cmd_control(sbi); err = create_flush_cmd_control(sbi);
if (err) if (err)
goto restore_gc; goto restore_gc;
@ -922,7 +937,7 @@ retry:
static int f2fs_fill_super(struct super_block *sb, void *data, int silent) static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
{ {
struct f2fs_sb_info *sbi; struct f2fs_sb_info *sbi;
struct f2fs_super_block *raw_super; struct f2fs_super_block *raw_super = NULL;
struct buffer_head *raw_super_buf; struct buffer_head *raw_super_buf;
struct inode *root; struct inode *root;
long err = -EINVAL; long err = -EINVAL;
@ -1123,7 +1138,7 @@ try_onemore:
* If filesystem is not mounted as read-only then * If filesystem is not mounted as read-only then
* do start the gc_thread. * do start the gc_thread.
*/ */
if (!f2fs_readonly(sb)) { if (test_opt(sbi, BG_GC) && !f2fs_readonly(sb)) {
/* After POR, we can run background GC thread.*/ /* After POR, we can run background GC thread.*/
err = start_gc_thread(sbi); err = start_gc_thread(sbi);
if (err) if (err)

View File

@ -83,7 +83,7 @@ static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name,
} }
if (strcmp(name, "") == 0) if (strcmp(name, "") == 0)
return -EINVAL; return -EINVAL;
return f2fs_getxattr(dentry->d_inode, type, name, buffer, size); return f2fs_getxattr(dentry->d_inode, type, name, buffer, size, NULL);
} }
static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name, static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name,
@ -398,7 +398,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
} }
int f2fs_getxattr(struct inode *inode, int index, const char *name, int f2fs_getxattr(struct inode *inode, int index, const char *name,
void *buffer, size_t buffer_size) void *buffer, size_t buffer_size, struct page *ipage)
{ {
struct f2fs_xattr_entry *entry; struct f2fs_xattr_entry *entry;
void *base_addr; void *base_addr;
@ -412,7 +412,7 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
if (len > F2FS_NAME_LEN) if (len > F2FS_NAME_LEN)
return -ERANGE; return -ERANGE;
base_addr = read_all_xattrs(inode, NULL); base_addr = read_all_xattrs(inode, ipage);
if (!base_addr) if (!base_addr)
return -ENOMEM; return -ENOMEM;

View File

@ -115,7 +115,8 @@ extern const struct xattr_handler *f2fs_xattr_handlers[];
extern int f2fs_setxattr(struct inode *, int, const char *, extern int f2fs_setxattr(struct inode *, int, const char *,
const void *, size_t, struct page *, int); const void *, size_t, struct page *, int);
extern int f2fs_getxattr(struct inode *, int, const char *, void *, size_t); extern int f2fs_getxattr(struct inode *, int, const char *, void *,
size_t, struct page *);
extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t); extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
#else #else
@ -126,7 +127,8 @@ static inline int f2fs_setxattr(struct inode *inode, int index,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline int f2fs_getxattr(struct inode *inode, int index, static inline int f2fs_getxattr(struct inode *inode, int index,
const char *name, void *buffer, size_t buffer_size) const char *name, void *buffer,
size_t buffer_size, struct page *dpage)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View File

@ -33,7 +33,8 @@
#define F2FS_META_INO(sbi) (sbi->meta_ino_num) #define F2FS_META_INO(sbi) (sbi->meta_ino_num)
/* This flag is used by node and meta inodes, and by recovery */ /* This flag is used by node and meta inodes, and by recovery */
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO) #define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
#define GFP_F2FS_HIGH_ZERO (GFP_NOFS | __GFP_ZERO | __GFP_HIGHMEM)
/* /*
* For further optimization on multi-head logs, on-disk layout supports maximum * For further optimization on multi-head logs, on-disk layout supports maximum
@ -170,14 +171,12 @@ struct f2fs_extent {
#define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ #define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */
#define F2FS_INLINE_DATA 0x02 /* file inline data flag */ #define F2FS_INLINE_DATA 0x02 /* file inline data flag */
#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */
#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */
#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ #define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
F2FS_INLINE_XATTR_ADDRS - 1)) F2FS_INLINE_XATTR_ADDRS - 1))
#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) -\
sizeof(__le32) * (DEF_ADDRS_PER_INODE + \
DEF_NIDS_PER_INODE - 1))
struct f2fs_inode { struct f2fs_inode {
__le16 i_mode; /* file mode */ __le16 i_mode; /* file mode */
__u8 i_advise; /* file hints */ __u8 i_advise; /* file hints */
@ -435,6 +434,24 @@ struct f2fs_dentry_block {
__u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
} __packed; } __packed;
/* for inline dir */
#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \
((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \
BITS_PER_BYTE + 1))
#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \
BITS_PER_BYTE - 1) / BITS_PER_BYTE)
#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \
((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \
NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE))
/* inline directory entry structure */
struct f2fs_inline_dentry {
__u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE];
__u8 reserved[INLINE_RESERVED_SIZE];
struct f2fs_dir_entry dentry[NR_INLINE_DENTRY];
__u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN];
} __packed;
/* file types used in inode_info->flags */ /* file types used in inode_info->flags */
enum { enum {
F2FS_FT_UNKNOWN, F2FS_FT_UNKNOWN,