Update f2fs to kernel.org:f2fs-stable 3.18 (2016-04-02)
This commit is contained in:
parent
2be4c680cb
commit
3c3684dec4
32 changed files with 9127 additions and 2852 deletions
|
@ -74,3 +74,41 @@ Date: March 2014
|
|||
Contact: "Jaegeuk Kim" <jaegeuk.kim@samsung.com>
|
||||
Description:
|
||||
Controls the memory footprint used by f2fs.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/trim_sections
|
||||
Date: February 2015
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description:
|
||||
Controls the trimming rate in batch mode.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/cp_interval
|
||||
Date: October 2015
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description:
|
||||
Controls the checkpoint timing.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/idle_interval
|
||||
Date: January 2016
|
||||
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
|
||||
Description:
|
||||
Controls the idle timing.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/ra_nid_pages
|
||||
Date: October 2015
|
||||
Contact: "Chao Yu" <chao2.yu@samsung.com>
|
||||
Description:
|
||||
Controls the count of nid pages to be readaheaded.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/dirty_nats_ratio
|
||||
Date: January 2016
|
||||
Contact: "Chao Yu" <chao2.yu@samsung.com>
|
||||
Description:
|
||||
Controls dirty nat entries ratio threshold, if current
|
||||
ratio exceeds configured threshold, checkpoint will
|
||||
be triggered for flushing dirty nat entries.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/lifetime_write_kbytes
|
||||
Date: January 2016
|
||||
Contact: "Shuoran Liu" <liushuoran@huawei.com>
|
||||
Description:
|
||||
Shows total written kbytes issued to disk.
|
||||
|
|
|
@ -131,6 +131,19 @@ nobarrier This option can be used if underlying storage guarantees
|
|||
If this option is set, no cache_flush commands are issued
|
||||
but f2fs still guarantees the write ordering of all the
|
||||
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.
|
||||
extent_cache Enable an extent cache based on rb-tree, it can cache
|
||||
as many as extent which map between contiguous logical
|
||||
address and physical address per inode, resulting in
|
||||
increasing the cache hit ratio. Set by default.
|
||||
noextent_cache Disable an extent cache based on rb-tree explicitly, see
|
||||
the above extent_cache mount option.
|
||||
noinline_data Disable the inline data feature, inline data feature is
|
||||
enabled by default.
|
||||
data_flush Enable data flushing before checkpoint in order to
|
||||
persist data of regular and symlink.
|
||||
|
||||
================================================================================
|
||||
DEBUGFS ENTRIES
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
config F2FS_FS
|
||||
tristate "F2FS filesystem support (EXPERIMENTAL)"
|
||||
tristate "F2FS filesystem support"
|
||||
depends on BLOCK
|
||||
select CRYPTO
|
||||
select CRYPTO_CRC32
|
||||
help
|
||||
F2FS is based on Log-structured File System (LFS), which supports
|
||||
versatile "flash-friendly" features. The design has been focused on
|
||||
|
@ -45,7 +47,7 @@ config F2FS_FS_POSIX_ACL
|
|||
default y
|
||||
help
|
||||
Posix Access Control Lists (ACLs) support permissions for users and
|
||||
gourps beyond the owner/group/world scheme.
|
||||
groups beyond the owner/group/world scheme.
|
||||
|
||||
To learn more about Access Control Lists, visit the POSIX ACLs for
|
||||
Linux website <http://acl.bestbits.at/>.
|
||||
|
@ -71,3 +73,24 @@ config F2FS_CHECK_FS
|
|||
Enables BUG_ONs which check the filesystem consistency in runtime.
|
||||
|
||||
If you want to improve the performance, say N.
|
||||
|
||||
config F2FS_FS_ENCRYPTION
|
||||
bool "F2FS Encryption"
|
||||
depends on F2FS_FS
|
||||
depends on F2FS_FS_XATTR
|
||||
select FS_ENCRYPTION
|
||||
help
|
||||
Enable encryption of f2fs files and directories. This
|
||||
feature is similar to ecryptfs, but it is more memory
|
||||
efficient since it avoids caching the encrypted and
|
||||
decrypted pages in the page cache.
|
||||
|
||||
config F2FS_IO_TRACE
|
||||
bool "F2FS IO tracer"
|
||||
depends on F2FS_FS
|
||||
depends on FUNCTION_TRACER
|
||||
help
|
||||
F2FS IO trace is based on a function trace, which gathers process
|
||||
information and block IO patterns in the filesystem level.
|
||||
|
||||
If unsure, say N.
|
||||
|
|
|
@ -2,6 +2,8 @@ obj-$(CONFIG_F2FS_FS) += f2fs.o
|
|||
|
||||
f2fs-y := dir.o file.o inode.o namei.o hash.o super.o inline.o
|
||||
f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o
|
||||
f2fs-y += shrinker.o extent_cache.o
|
||||
f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
|
||||
f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
|
||||
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
|
||||
f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
|
||||
|
|
152
fs/f2fs/acl.c
152
fs/f2fs/acl.c
|
@ -62,7 +62,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
|
|||
if (count == 0)
|
||||
return NULL;
|
||||
|
||||
acl = posix_acl_alloc(count, GFP_KERNEL);
|
||||
acl = posix_acl_alloc(count, GFP_NOFS);
|
||||
if (!acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -116,7 +116,7 @@ static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size)
|
|||
int i;
|
||||
|
||||
f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count *
|
||||
sizeof(struct f2fs_acl_entry), GFP_KERNEL);
|
||||
sizeof(struct f2fs_acl_entry), GFP_NOFS);
|
||||
if (!f2fs_acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -162,7 +162,8 @@ fail:
|
|||
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;
|
||||
void *value = NULL;
|
||||
|
@ -172,12 +173,13 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
|
|||
if (type == ACL_TYPE_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) {
|
||||
value = kmalloc(retval, GFP_F2FS_ZERO);
|
||||
if (!value)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
retval = f2fs_getxattr(inode, name_index, "", value, retval);
|
||||
retval = f2fs_getxattr(inode, name_index, "", value,
|
||||
retval, dpage);
|
||||
}
|
||||
|
||||
if (retval > 0)
|
||||
|
@ -194,6 +196,11 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
|
|||
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,
|
||||
struct posix_acl *acl, struct page *ipage)
|
||||
{
|
||||
|
@ -229,7 +236,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
|||
if (acl) {
|
||||
value = f2fs_acl_to_disk(acl, &size);
|
||||
if (IS_ERR(value)) {
|
||||
cond_clear_inode_flag(fi, FI_ACL_MODE);
|
||||
clear_inode_flag(fi, FI_ACL_MODE);
|
||||
return (int)PTR_ERR(value);
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +247,7 @@ static int __f2fs_set_acl(struct inode *inode, int type,
|
|||
if (!error)
|
||||
set_cached_acl(inode, type, acl);
|
||||
|
||||
cond_clear_inode_flag(fi, FI_ACL_MODE);
|
||||
clear_inode_flag(fi, FI_ACL_MODE);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -249,12 +256,135 @@ int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||
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;
|
||||
struct posix_acl *clone;
|
||||
int ret;
|
||||
|
||||
*acl = NULL;
|
||||
*default_acl = NULL;
|
||||
|
||||
if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
|
||||
return 0;
|
||||
|
||||
p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
|
||||
if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
|
||||
*mode &= ~current_umask();
|
||||
return 0;
|
||||
}
|
||||
if (IS_ERR(p))
|
||||
return PTR_ERR(p);
|
||||
|
||||
clone = f2fs_acl_clone(p, GFP_NOFS);
|
||||
if (!clone)
|
||||
goto no_mem;
|
||||
|
||||
ret = f2fs_acl_create_masq(clone, mode);
|
||||
if (ret < 0)
|
||||
goto no_mem_clone;
|
||||
|
||||
if (ret == 0)
|
||||
posix_acl_release(clone);
|
||||
else
|
||||
*acl = clone;
|
||||
|
||||
if (!S_ISDIR(*mode))
|
||||
posix_acl_release(p);
|
||||
else
|
||||
*default_acl = p;
|
||||
|
||||
return 0;
|
||||
|
||||
no_mem_clone:
|
||||
posix_acl_release(clone);
|
||||
no_mem:
|
||||
posix_acl_release(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
return error;
|
||||
|
||||
|
@ -264,7 +394,7 @@ int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage)
|
|||
posix_acl_release(default_acl);
|
||||
}
|
||||
if (acl) {
|
||||
if (error)
|
||||
if (!error)
|
||||
error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl,
|
||||
ipage);
|
||||
posix_acl_release(acl);
|
||||
|
|
|
@ -37,15 +37,16 @@ struct f2fs_acl_header {
|
|||
#ifdef CONFIG_F2FS_FS_POSIX_ACL
|
||||
|
||||
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_init_acl(struct inode *, struct inode *, struct page *);
|
||||
extern int f2fs_set_acl(struct inode *, struct posix_acl *, int);
|
||||
extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
|
||||
struct page *);
|
||||
#else
|
||||
#define f2fs_check_acl NULL
|
||||
#define f2fs_get_acl NULL
|
||||
#define f2fs_set_acl NULL
|
||||
|
||||
static inline int f2fs_init_acl(struct inode *inode, struct inode *dir,
|
||||
struct page *page)
|
||||
struct page *ipage, struct page *dpage)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
1543
fs/f2fs/data.c
1543
fs/f2fs/data.c
File diff suppressed because it is too large
Load diff
146
fs/f2fs/debug.c
146
fs/f2fs/debug.c
|
@ -33,19 +33,31 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
int i;
|
||||
|
||||
/* validation check of the segment numbers */
|
||||
si->hit_ext = sbi->read_hit_ext;
|
||||
si->total_ext = sbi->total_hit_ext;
|
||||
si->hit_largest = atomic64_read(&sbi->read_hit_largest);
|
||||
si->hit_cached = atomic64_read(&sbi->read_hit_cached);
|
||||
si->hit_rbtree = atomic64_read(&sbi->read_hit_rbtree);
|
||||
si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree;
|
||||
si->total_ext = atomic64_read(&sbi->total_hit_ext);
|
||||
si->ext_tree = atomic_read(&sbi->total_ext_tree);
|
||||
si->zombie_tree = atomic_read(&sbi->total_zombie_tree);
|
||||
si->ext_node = atomic_read(&sbi->total_ext_node);
|
||||
si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES);
|
||||
si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS);
|
||||
si->ndirty_dirs = sbi->n_dirty_dirs;
|
||||
si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META);
|
||||
si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA);
|
||||
si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE];
|
||||
si->ndirty_files = sbi->ndirty_inode[FILE_INODE];
|
||||
si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
|
||||
si->wb_pages = get_pages(sbi, F2FS_WRITEBACK);
|
||||
si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
|
||||
si->rsvd_segs = reserved_segments(sbi);
|
||||
si->overp_segs = overprovision_segments(sbi);
|
||||
si->valid_count = valid_user_blocks(sbi);
|
||||
si->valid_node_count = valid_node_count(sbi);
|
||||
si->valid_inode_count = valid_inode_count(sbi);
|
||||
si->inline_inode = sbi->inline_inode;
|
||||
si->inline_xattr = atomic_read(&sbi->inline_xattr);
|
||||
si->inline_inode = atomic_read(&sbi->inline_inode);
|
||||
si->inline_dir = atomic_read(&sbi->inline_dir);
|
||||
si->utilization = utilization(sbi);
|
||||
|
||||
si->free_segs = free_segments(sbi);
|
||||
|
@ -55,7 +67,9 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->node_pages = NODE_MAPPING(sbi)->nrpages;
|
||||
si->meta_pages = META_MAPPING(sbi)->nrpages;
|
||||
si->nats = NM_I(sbi)->nat_cnt;
|
||||
si->sits = SIT_I(sbi)->dirty_sentries;
|
||||
si->dirty_nats = NM_I(sbi)->dirty_nat_cnt;
|
||||
si->sits = MAIN_SEGS(sbi);
|
||||
si->dirty_sits = SIT_I(sbi)->dirty_sentries;
|
||||
si->fnids = NM_I(sbi)->fcnt;
|
||||
si->bg_gc = sbi->bg_gc;
|
||||
si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg)
|
||||
|
@ -77,6 +91,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
si->segment_count[i] = sbi->segment_count[i];
|
||||
si->block_count[i] = sbi->block_count[i];
|
||||
}
|
||||
|
||||
si->inplace_count = atomic_read(&sbi->inplace_count);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -85,13 +101,14 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
|||
static void update_sit_info(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||
unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist;
|
||||
unsigned long long blks_per_sec, hblks_per_sec, total_vblocks;
|
||||
unsigned long long bimodal, dist;
|
||||
unsigned int segno, vblocks;
|
||||
int ndirty = 0;
|
||||
|
||||
bimodal = 0;
|
||||
total_vblocks = 0;
|
||||
blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg);
|
||||
blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
|
||||
hblks_per_sec = blks_per_sec / 2;
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
|
||||
vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
|
||||
|
@ -103,10 +120,10 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
|
|||
ndirty++;
|
||||
}
|
||||
}
|
||||
dist = MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100;
|
||||
si->bimodal = bimodal / dist;
|
||||
dist = div_u64(MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec, 100);
|
||||
si->bimodal = div64_u64(bimodal, dist);
|
||||
if (si->dirty_count)
|
||||
si->avg_vblocks = total_vblocks / ndirty;
|
||||
si->avg_vblocks = div_u64(total_vblocks, ndirty);
|
||||
else
|
||||
si->avg_vblocks = 0;
|
||||
}
|
||||
|
@ -118,6 +135,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
{
|
||||
struct f2fs_stat_info *si = F2FS_STAT(sbi);
|
||||
unsigned npages;
|
||||
int i;
|
||||
|
||||
if (si->base_mem)
|
||||
goto get_cache;
|
||||
|
@ -133,7 +151,8 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
si->base_mem += sizeof(struct sit_info);
|
||||
si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry);
|
||||
si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi));
|
||||
si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi);
|
||||
si->base_mem += 3 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi);
|
||||
si->base_mem += SIT_VBLOCK_MAP_SIZE;
|
||||
if (sbi->segs_per_sec > 1)
|
||||
si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry);
|
||||
si->base_mem += __bitmap_size(sbi, SIT_BITMAP);
|
||||
|
@ -156,19 +175,35 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
|||
si->base_mem += sizeof(struct f2fs_nm_info);
|
||||
si->base_mem += __bitmap_size(sbi, NAT_BITMAP);
|
||||
|
||||
/* build gc */
|
||||
si->base_mem += sizeof(struct f2fs_gc_kthread);
|
||||
|
||||
get_cache:
|
||||
si->cache_mem = 0;
|
||||
|
||||
/* build gc */
|
||||
if (sbi->gc_thread)
|
||||
si->cache_mem += sizeof(struct f2fs_gc_kthread);
|
||||
|
||||
/* build merge flush thread */
|
||||
if (SM_I(sbi)->cmd_control_info)
|
||||
si->cache_mem += sizeof(struct flush_cmd_control);
|
||||
|
||||
/* free nids */
|
||||
si->cache_mem = NM_I(sbi)->fcnt;
|
||||
si->cache_mem += NM_I(sbi)->nat_cnt;
|
||||
si->cache_mem += NM_I(sbi)->fcnt * sizeof(struct free_nid);
|
||||
si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry);
|
||||
si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
|
||||
sizeof(struct nat_entry_set);
|
||||
si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages);
|
||||
for (i = 0; i <= UPDATE_INO; i++)
|
||||
si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
|
||||
si->cache_mem += atomic_read(&sbi->total_ext_tree) *
|
||||
sizeof(struct extent_tree);
|
||||
si->cache_mem += atomic_read(&sbi->total_ext_node) *
|
||||
sizeof(struct extent_node);
|
||||
|
||||
si->page_mem = 0;
|
||||
npages = NODE_MAPPING(sbi)->nrpages;
|
||||
si->cache_mem += npages << PAGE_CACHE_SHIFT;
|
||||
si->page_mem += (unsigned long long)npages << PAGE_CACHE_SHIFT;
|
||||
npages = META_MAPPING(sbi)->nrpages;
|
||||
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->page_mem += (unsigned long long)npages << PAGE_CACHE_SHIFT;
|
||||
}
|
||||
|
||||
static int stat_show(struct seq_file *s, void *v)
|
||||
|
@ -198,8 +233,12 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
seq_printf(s, "Other: %u)\n - Data: %u\n",
|
||||
si->valid_node_count - si->valid_inode_count,
|
||||
si->valid_count - si->valid_node_count);
|
||||
seq_printf(s, " - Inline_xattr Inode: %u\n",
|
||||
si->inline_xattr);
|
||||
seq_printf(s, " - Inline_data Inode: %u\n",
|
||||
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",
|
||||
si->main_area_segs, si->main_area_sections,
|
||||
si->main_area_zones);
|
||||
|
@ -233,25 +272,43 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
si->dirty_count);
|
||||
seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n",
|
||||
si->prefree_count, si->free_segs, si->free_secs);
|
||||
seq_printf(s, "CP calls: %d\n", si->cp_count);
|
||||
seq_printf(s, "CP calls: %d (BG: %d)\n",
|
||||
si->cp_count, si->bg_cp_count);
|
||||
seq_printf(s, "GC calls: %d (BG: %d)\n",
|
||||
si->call_count, si->bg_gc);
|
||||
seq_printf(s, " - data segments : %d\n", si->data_segs);
|
||||
seq_printf(s, " - node segments : %d\n", si->node_segs);
|
||||
seq_printf(s, "Try to move %d blocks\n", si->tot_blks);
|
||||
seq_printf(s, " - data blocks : %d\n", si->data_blks);
|
||||
seq_printf(s, " - node blocks : %d\n", si->node_blks);
|
||||
seq_printf(s, "\nExtent Hit Ratio: %d / %d\n",
|
||||
si->hit_ext, si->total_ext);
|
||||
seq_printf(s, " - data segments : %d (%d)\n",
|
||||
si->data_segs, si->bg_data_segs);
|
||||
seq_printf(s, " - node segments : %d (%d)\n",
|
||||
si->node_segs, si->bg_node_segs);
|
||||
seq_printf(s, "Try to move %d blocks (BG: %d)\n", si->tot_blks,
|
||||
si->bg_data_blks + si->bg_node_blks);
|
||||
seq_printf(s, " - data blocks : %d (%d)\n", si->data_blks,
|
||||
si->bg_data_blks);
|
||||
seq_printf(s, " - node blocks : %d (%d)\n", si->node_blks,
|
||||
si->bg_node_blks);
|
||||
seq_puts(s, "\nExtent Cache:\n");
|
||||
seq_printf(s, " - Hit Count: L1-1:%llu L1-2:%llu L2:%llu\n",
|
||||
si->hit_largest, si->hit_cached,
|
||||
si->hit_rbtree);
|
||||
seq_printf(s, " - Hit Ratio: %llu%% (%llu / %llu)\n",
|
||||
!si->total_ext ? 0 :
|
||||
div64_u64(si->hit_total * 100, si->total_ext),
|
||||
si->hit_total, si->total_ext);
|
||||
seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n",
|
||||
si->ext_tree, si->zombie_tree, si->ext_node);
|
||||
seq_puts(s, "\nBalancing F2FS Async:\n");
|
||||
seq_printf(s, " - inmem: %4d, wb: %4d\n",
|
||||
si->inmem_pages, si->wb_pages);
|
||||
seq_printf(s, " - nodes: %4d in %4d\n",
|
||||
si->ndirty_node, si->node_pages);
|
||||
seq_printf(s, " - dents: %4d in dirs:%4d\n",
|
||||
si->ndirty_dent, si->ndirty_dirs);
|
||||
seq_printf(s, " - datas: %4d in files:%4d\n",
|
||||
si->ndirty_data, si->ndirty_files);
|
||||
seq_printf(s, " - meta: %4d in %4d\n",
|
||||
si->ndirty_meta, si->meta_pages);
|
||||
seq_printf(s, " - NATs: %9d\n - SITs: %9d\n",
|
||||
si->nats, si->sits);
|
||||
seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n",
|
||||
si->dirty_nats, si->nats, si->dirty_sits, si->sits);
|
||||
seq_printf(s, " - free_nids: %9d\n",
|
||||
si->fnids);
|
||||
seq_puts(s, "\nDistribution of User Blocks:");
|
||||
|
@ -269,6 +326,7 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
for (j = 0; j < si->util_free; j++)
|
||||
seq_putc(s, '-');
|
||||
seq_puts(s, "]\n\n");
|
||||
seq_printf(s, "IPU: %u blocks\n", si->inplace_count);
|
||||
seq_printf(s, "SSR: %u blocks in %u segments\n",
|
||||
si->block_count[SSR], si->segment_count[SSR]);
|
||||
seq_printf(s, "LFS: %u blocks in %u segments\n",
|
||||
|
@ -281,9 +339,14 @@ static int stat_show(struct seq_file *s, void *v)
|
|||
|
||||
/* memory footprint */
|
||||
update_mem_info(si->sbi);
|
||||
seq_printf(s, "\nMemory: %u KB = static: %u + cached: %u\n",
|
||||
(si->base_mem + si->cache_mem) >> 10,
|
||||
si->base_mem >> 10, si->cache_mem >> 10);
|
||||
seq_printf(s, "\nMemory: %llu KB\n",
|
||||
(si->base_mem + si->cache_mem + si->page_mem) >> 10);
|
||||
seq_printf(s, " - static: %llu KB\n",
|
||||
si->base_mem >> 10);
|
||||
seq_printf(s, " - cached: %llu KB\n",
|
||||
si->cache_mem >> 10);
|
||||
seq_printf(s, " - paged : %llu KB\n",
|
||||
si->page_mem >> 10);
|
||||
}
|
||||
mutex_unlock(&f2fs_stat_mutex);
|
||||
return 0;
|
||||
|
@ -321,6 +384,16 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
|
|||
si->sbi = sbi;
|
||||
sbi->stat_info = si;
|
||||
|
||||
atomic64_set(&sbi->total_hit_ext, 0);
|
||||
atomic64_set(&sbi->read_hit_rbtree, 0);
|
||||
atomic64_set(&sbi->read_hit_largest, 0);
|
||||
atomic64_set(&sbi->read_hit_cached, 0);
|
||||
|
||||
atomic_set(&sbi->inline_xattr, 0);
|
||||
atomic_set(&sbi->inline_inode, 0);
|
||||
atomic_set(&sbi->inline_dir, 0);
|
||||
atomic_set(&sbi->inplace_count, 0);
|
||||
|
||||
mutex_lock(&f2fs_stat_mutex);
|
||||
list_add_tail(&si->stat_list, &f2fs_stat_list);
|
||||
mutex_unlock(&f2fs_stat_mutex);
|
||||
|
@ -339,20 +412,23 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi)
|
|||
kfree(si);
|
||||
}
|
||||
|
||||
void __init f2fs_create_root_stats(void)
|
||||
int __init f2fs_create_root_stats(void)
|
||||
{
|
||||
struct dentry *file;
|
||||
|
||||
f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL);
|
||||
if (!f2fs_debugfs_root)
|
||||
return;
|
||||
return -ENOMEM;
|
||||
|
||||
file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root,
|
||||
NULL, &stat_fops);
|
||||
if (!file) {
|
||||
debugfs_remove(f2fs_debugfs_root);
|
||||
f2fs_debugfs_root = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f2fs_destroy_root_stats(void)
|
||||
|
|
577
fs/f2fs/dir.c
577
fs/f2fs/dir.c
|
@ -37,7 +37,7 @@ static unsigned int bucket_blocks(unsigned int level)
|
|||
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_REG_FILE] = DT_REG,
|
||||
[F2FS_FT_DIR] = DT_DIR,
|
||||
|
@ -59,9 +59,8 @@ static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = {
|
|||
[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, umode_t mode)
|
||||
{
|
||||
umode_t mode = inode->i_mode;
|
||||
de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
|
||||
}
|
||||
|
||||
|
@ -77,81 +76,101 @@ static unsigned long dir_block_index(unsigned int level,
|
|||
return bidx;
|
||||
}
|
||||
|
||||
static bool early_match_name(size_t namelen, f2fs_hash_t namehash,
|
||||
struct f2fs_dir_entry *de)
|
||||
static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||
struct fscrypt_name *fname,
|
||||
f2fs_hash_t namehash,
|
||||
int *max_slots,
|
||||
struct page **res_page)
|
||||
{
|
||||
if (le16_to_cpu(de->name_len) != namelen)
|
||||
return false;
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
struct f2fs_dir_entry *de;
|
||||
struct f2fs_dentry_ptr d;
|
||||
|
||||
if (de->hash_code != namehash)
|
||||
return false;
|
||||
dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
|
||||
|
||||
return true;
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
de = find_target_dentry(fname, namehash, 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;
|
||||
}
|
||||
|
||||
static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
|
||||
struct qstr *name, int *max_slots,
|
||||
f2fs_hash_t namehash, struct page **res_page)
|
||||
struct f2fs_dir_entry *find_target_dentry(struct fscrypt_name *fname,
|
||||
f2fs_hash_t namehash, int *max_slots,
|
||||
struct f2fs_dentry_ptr *d)
|
||||
{
|
||||
struct f2fs_dir_entry *de;
|
||||
unsigned long bit_pos = 0;
|
||||
struct f2fs_dentry_block *dentry_blk = kmap(dentry_page);
|
||||
const void *dentry_bits = &dentry_blk->dentry_bitmap;
|
||||
int max_len = 0;
|
||||
struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_str *name = &fname->disk_name;
|
||||
|
||||
while (bit_pos < NR_DENTRY_IN_BLOCK) {
|
||||
if (!test_bit_le(bit_pos, dentry_bits)) {
|
||||
if (bit_pos == 0)
|
||||
max_len = 1;
|
||||
else if (!test_bit_le(bit_pos - 1, dentry_bits))
|
||||
max_len++;
|
||||
if (max_slots)
|
||||
*max_slots = 0;
|
||||
while (bit_pos < d->max) {
|
||||
if (!test_bit_le(bit_pos, d->bitmap)) {
|
||||
bit_pos++;
|
||||
max_len++;
|
||||
continue;
|
||||
}
|
||||
de = &dentry_blk->dentry[bit_pos];
|
||||
if (early_match_name(name->len, namehash, de)) {
|
||||
if (!memcmp(dentry_blk->filename[bit_pos],
|
||||
name->name,
|
||||
name->len)) {
|
||||
*res_page = dentry_page;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
if (max_len > *max_slots) {
|
||||
*max_slots = max_len;
|
||||
max_len = 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(F2FS_P_SB(dentry_page), !de->name_len);
|
||||
de = &d->dentry[bit_pos];
|
||||
|
||||
/* encrypted case */
|
||||
de_name.name = d->filename[bit_pos];
|
||||
de_name.len = le16_to_cpu(de->name_len);
|
||||
|
||||
/* show encrypted name */
|
||||
if (fname->hash) {
|
||||
if (de->hash_code == fname->hash)
|
||||
goto found;
|
||||
} else if (de_name.len == name->len &&
|
||||
de->hash_code == namehash &&
|
||||
!memcmp(de_name.name, name->name, name->len))
|
||||
goto found;
|
||||
|
||||
if (max_slots && max_len > *max_slots)
|
||||
*max_slots = max_len;
|
||||
max_len = 0;
|
||||
|
||||
/* remain bug on condition */
|
||||
if (unlikely(!de->name_len))
|
||||
d->max = -1;
|
||||
|
||||
bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
|
||||
}
|
||||
|
||||
de = NULL;
|
||||
kunmap(dentry_page);
|
||||
found:
|
||||
if (max_len > *max_slots)
|
||||
if (max_slots && max_len > *max_slots)
|
||||
*max_slots = max_len;
|
||||
return de;
|
||||
}
|
||||
|
||||
static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
||||
unsigned int level, struct qstr *name,
|
||||
f2fs_hash_t namehash, struct page **res_page)
|
||||
unsigned int level,
|
||||
struct fscrypt_name *fname,
|
||||
struct page **res_page)
|
||||
{
|
||||
int s = GET_DENTRY_SLOTS(name->len);
|
||||
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
||||
int s = GET_DENTRY_SLOTS(name.len);
|
||||
unsigned int nbucket, nblock;
|
||||
unsigned int bidx, end_block;
|
||||
struct page *dentry_page;
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
bool room = false;
|
||||
int max_slots = 0;
|
||||
int max_slots;
|
||||
f2fs_hash_t namehash;
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
|
||||
namehash = f2fs_dentry_hash(&name);
|
||||
|
||||
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
|
||||
nblock = bucket_blocks(level);
|
||||
|
@ -162,14 +181,14 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
|
|||
|
||||
for (; bidx < end_block; bidx++) {
|
||||
/* no need to allocate new dentry pages to all the indices */
|
||||
dentry_page = find_data_page(dir, bidx, true);
|
||||
dentry_page = find_data_page(dir, bidx);
|
||||
if (IS_ERR(dentry_page)) {
|
||||
room = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
de = find_in_block(dentry_page, name, &max_slots,
|
||||
namehash, res_page);
|
||||
de = find_in_block(dentry_page, fname, namehash, &max_slots,
|
||||
res_page);
|
||||
if (de)
|
||||
break;
|
||||
|
||||
|
@ -197,27 +216,42 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
|
|||
{
|
||||
unsigned long npages = dir_blocks(dir);
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
f2fs_hash_t name_hash;
|
||||
unsigned int max_depth;
|
||||
unsigned int level;
|
||||
|
||||
if (npages == 0)
|
||||
return NULL;
|
||||
struct fscrypt_name fname;
|
||||
int err;
|
||||
|
||||
*res_page = NULL;
|
||||
|
||||
name_hash = f2fs_dentry_hash(child);
|
||||
err = fscrypt_setup_filename(dir, child, 1, &fname);
|
||||
if (err)
|
||||
return NULL;
|
||||
|
||||
if (f2fs_has_inline_dentry(dir)) {
|
||||
de = find_in_inline_dir(dir, &fname, res_page);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (npages == 0)
|
||||
goto out;
|
||||
|
||||
max_depth = F2FS_I(dir)->i_current_depth;
|
||||
if (unlikely(max_depth > MAX_DIR_HASH_DEPTH)) {
|
||||
f2fs_msg(F2FS_I_SB(dir)->sb, KERN_WARNING,
|
||||
"Corrupted max_depth of %lu: %u",
|
||||
dir->i_ino, max_depth);
|
||||
max_depth = MAX_DIR_HASH_DEPTH;
|
||||
F2FS_I(dir)->i_current_depth = max_depth;
|
||||
mark_inode_dirty(dir);
|
||||
}
|
||||
|
||||
for (level = 0; level < max_depth; level++) {
|
||||
de = find_in_level(dir, level, child, name_hash, res_page);
|
||||
de = find_in_level(dir, level, &fname, res_page);
|
||||
if (de)
|
||||
break;
|
||||
}
|
||||
if (!de && F2FS_I(dir)->chash != name_hash) {
|
||||
F2FS_I(dir)->chash = name_hash;
|
||||
F2FS_I(dir)->clevel = level - 1;
|
||||
}
|
||||
out:
|
||||
fscrypt_free_filename(&fname);
|
||||
return de;
|
||||
}
|
||||
|
||||
|
@ -227,7 +261,10 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p)
|
|||
struct f2fs_dir_entry *de;
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
|
||||
page = get_lock_data_page(dir, 0);
|
||||
if (f2fs_has_inline_dentry(dir))
|
||||
return f2fs_parent_inline_dir(dir, p);
|
||||
|
||||
page = get_lock_data_page(dir, 0, false);
|
||||
if (IS_ERR(page))
|
||||
return NULL;
|
||||
|
||||
|
@ -247,7 +284,7 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr)
|
|||
de = f2fs_find_entry(dir, qstr, &page);
|
||||
if (de) {
|
||||
res = le32_to_cpu(de->ino);
|
||||
kunmap(page);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
}
|
||||
|
||||
|
@ -257,11 +294,12 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr)
|
|||
void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
|
||||
struct page *page, struct inode *inode)
|
||||
{
|
||||
enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA;
|
||||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, type, true);
|
||||
de->ino = cpu_to_le32(inode->i_ino);
|
||||
set_de_type(de, inode);
|
||||
kunmap(page);
|
||||
set_de_type(de, inode->i_mode);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
set_page_dirty(page);
|
||||
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty(dir);
|
||||
|
@ -273,7 +311,7 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage)
|
|||
{
|
||||
struct f2fs_inode *ri;
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
/* copy name info. to this inode page */
|
||||
ri = F2FS_INODE(ipage);
|
||||
|
@ -282,10 +320,14 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage)
|
|||
set_page_dirty(ipage);
|
||||
}
|
||||
|
||||
int update_dent_inode(struct inode *inode, const struct qstr *name)
|
||||
int update_dent_inode(struct inode *inode, struct inode *to,
|
||||
const struct qstr *name)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
if (file_enc_name(to))
|
||||
return 0;
|
||||
|
||||
page = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
@ -296,36 +338,38 @@ int update_dent_inode(struct inode *inode, const struct qstr *name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void do_make_empty_dir(struct inode *inode, struct inode *parent,
|
||||
struct f2fs_dentry_ptr *d)
|
||||
{
|
||||
struct qstr dot = QSTR_INIT(".", 1);
|
||||
struct qstr dotdot = QSTR_INIT("..", 2);
|
||||
|
||||
/* update dirent of "." */
|
||||
f2fs_update_dentry(inode->i_ino, inode->i_mode, d, &dot, 0, 0);
|
||||
|
||||
/* update dirent of ".." */
|
||||
f2fs_update_dentry(parent->i_ino, parent->i_mode, d, &dotdot, 0, 1);
|
||||
}
|
||||
|
||||
static int make_empty_dir(struct inode *inode,
|
||||
struct inode *parent, struct page *page)
|
||||
{
|
||||
struct page *dentry_page;
|
||||
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);
|
||||
if (IS_ERR(dentry_page))
|
||||
return PTR_ERR(dentry_page);
|
||||
|
||||
|
||||
dentry_blk = kmap_atomic(dentry_page);
|
||||
|
||||
de = &dentry_blk->dentry[0];
|
||||
de->name_len = cpu_to_le16(1);
|
||||
de->hash_code = 0;
|
||||
de->ino = cpu_to_le32(inode->i_ino);
|
||||
memcpy(dentry_blk->filename[0], ".", 1);
|
||||
set_de_type(de, inode);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
do_make_empty_dir(inode, parent, &d);
|
||||
|
||||
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);
|
||||
|
||||
set_page_dirty(dentry_page);
|
||||
|
@ -333,8 +377,8 @@ static int make_empty_dir(struct inode *inode,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct page *init_inode_metadata(struct inode *inode,
|
||||
struct inode *dir, const struct qstr *name)
|
||||
struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
|
||||
const struct qstr *name, struct page *dpage)
|
||||
{
|
||||
struct page *page;
|
||||
int err;
|
||||
|
@ -350,13 +394,19 @@ static struct page *init_inode_metadata(struct inode *inode,
|
|||
goto error;
|
||||
}
|
||||
|
||||
err = f2fs_init_acl(inode, dir, page);
|
||||
err = f2fs_init_acl(inode, dir, page, dpage);
|
||||
if (err)
|
||||
goto put_error;
|
||||
|
||||
err = f2fs_init_security(inode, dir, name, page);
|
||||
if (err)
|
||||
goto put_error;
|
||||
|
||||
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) {
|
||||
err = fscrypt_inherit_context(dir, inode, page, false);
|
||||
if (err)
|
||||
goto put_error;
|
||||
}
|
||||
} else {
|
||||
page = get_node_page(F2FS_I_SB(dir), inode->i_ino);
|
||||
if (IS_ERR(page))
|
||||
|
@ -390,15 +440,15 @@ error:
|
|||
/* once the failed inode becomes a bad inode, i_mode is S_IFREG */
|
||||
truncate_inode_pages(&inode->i_data, 0);
|
||||
truncate_blocks(inode, 0, false);
|
||||
remove_dirty_dir_inode(inode);
|
||||
remove_dirty_inode(inode);
|
||||
remove_inode_page(inode);
|
||||
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)
|
||||
{
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
|
||||
if (inode && is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
inc_nlink(dir);
|
||||
set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
|
||||
|
@ -413,58 +463,92 @@ static void update_parent_metadata(struct inode *dir, struct inode *inode,
|
|||
set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
|
||||
}
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK))
|
||||
if (inode && is_inode_flag_set(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 zero_start, zero_end;
|
||||
next:
|
||||
zero_start = find_next_zero_bit_le(&dentry_blk->dentry_bitmap,
|
||||
NR_DENTRY_IN_BLOCK,
|
||||
bit_start);
|
||||
if (zero_start >= NR_DENTRY_IN_BLOCK)
|
||||
return NR_DENTRY_IN_BLOCK;
|
||||
zero_start = find_next_zero_bit_le(bitmap, max_slots, bit_start);
|
||||
if (zero_start >= max_slots)
|
||||
return max_slots;
|
||||
|
||||
zero_end = find_next_bit_le(&dentry_blk->dentry_bitmap,
|
||||
NR_DENTRY_IN_BLOCK,
|
||||
zero_start);
|
||||
zero_end = find_next_bit_le(bitmap, max_slots, zero_start);
|
||||
if (zero_end - zero_start >= slots)
|
||||
return zero_start;
|
||||
|
||||
bit_start = zero_end + 1;
|
||||
|
||||
if (zero_end + 1 >= NR_DENTRY_IN_BLOCK)
|
||||
return NR_DENTRY_IN_BLOCK;
|
||||
if (zero_end + 1 >= max_slots)
|
||||
return max_slots;
|
||||
goto next;
|
||||
}
|
||||
|
||||
void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d,
|
||||
const struct qstr *name, f2fs_hash_t name_hash,
|
||||
unsigned int bit_pos)
|
||||
{
|
||||
struct f2fs_dir_entry *de;
|
||||
int slots = GET_DENTRY_SLOTS(name->len);
|
||||
int i;
|
||||
|
||||
de = &d->dentry[bit_pos];
|
||||
de->hash_code = name_hash;
|
||||
de->name_len = cpu_to_le16(name->len);
|
||||
memcpy(d->filename[bit_pos], name->name, name->len);
|
||||
de->ino = cpu_to_le32(ino);
|
||||
set_de_type(de, mode);
|
||||
for (i = 0; i < slots; i++) {
|
||||
test_and_set_bit_le(bit_pos + i, (void *)d->bitmap);
|
||||
/* avoid wrong garbage data for readdir */
|
||||
if (i)
|
||||
(de + i)->name_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Caller should grab and release a rwsem by calling f2fs_lock_op() and
|
||||
* f2fs_unlock_op().
|
||||
*/
|
||||
int __f2fs_add_link(struct inode *dir, const struct qstr *name,
|
||||
struct inode *inode)
|
||||
struct inode *inode, nid_t ino, umode_t mode)
|
||||
{
|
||||
unsigned int bit_pos;
|
||||
unsigned int level;
|
||||
unsigned int current_depth;
|
||||
unsigned long bidx, block;
|
||||
f2fs_hash_t dentry_hash;
|
||||
struct f2fs_dir_entry *de;
|
||||
unsigned int nbucket, nblock;
|
||||
size_t namelen = name->len;
|
||||
struct page *dentry_page = NULL;
|
||||
struct f2fs_dentry_block *dentry_blk = NULL;
|
||||
int slots = GET_DENTRY_SLOTS(namelen);
|
||||
struct page *page;
|
||||
int err = 0;
|
||||
int i;
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct page *page = NULL;
|
||||
struct fscrypt_name fname;
|
||||
struct qstr new_name;
|
||||
int slots, err;
|
||||
|
||||
err = fscrypt_setup_filename(dir, name, 0, &fname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
new_name.name = fname_name(&fname);
|
||||
new_name.len = fname_len(&fname);
|
||||
|
||||
if (f2fs_has_inline_dentry(dir)) {
|
||||
err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode);
|
||||
if (!err || err != -EAGAIN)
|
||||
goto out;
|
||||
else
|
||||
err = 0;
|
||||
}
|
||||
|
||||
dentry_hash = f2fs_dentry_hash(name);
|
||||
level = 0;
|
||||
slots = GET_DENTRY_SLOTS(new_name.len);
|
||||
dentry_hash = f2fs_dentry_hash(&new_name);
|
||||
|
||||
current_depth = F2FS_I(dir)->i_current_depth;
|
||||
if (F2FS_I(dir)->chash == dentry_hash) {
|
||||
level = F2FS_I(dir)->clevel;
|
||||
|
@ -472,8 +556,10 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
|
|||
}
|
||||
|
||||
start:
|
||||
if (unlikely(current_depth == MAX_DIR_HASH_DEPTH))
|
||||
return -ENOSPC;
|
||||
if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) {
|
||||
err = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Increase the depth, if required */
|
||||
if (level == current_depth)
|
||||
|
@ -487,11 +573,14 @@ start:
|
|||
|
||||
for (block = bidx; block <= (bidx + nblock - 1); block++) {
|
||||
dentry_page = get_new_data_page(dir, NULL, block, true);
|
||||
if (IS_ERR(dentry_page))
|
||||
return PTR_ERR(dentry_page);
|
||||
if (IS_ERR(dentry_page)) {
|
||||
err = PTR_ERR(dentry_page);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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)
|
||||
goto add_dentry;
|
||||
|
||||
|
@ -503,32 +592,35 @@ start:
|
|||
++level;
|
||||
goto start;
|
||||
add_dentry:
|
||||
f2fs_wait_on_page_writeback(dentry_page, DATA);
|
||||
f2fs_wait_on_page_writeback(dentry_page, DATA, true);
|
||||
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
page = init_inode_metadata(inode, dir, name);
|
||||
if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
if (inode) {
|
||||
down_write(&F2FS_I(inode)->i_sem);
|
||||
page = init_inode_metadata(inode, dir, &new_name, NULL);
|
||||
if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
}
|
||||
if (f2fs_encrypted_inode(dir))
|
||||
file_set_enc_name(inode);
|
||||
}
|
||||
de = &dentry_blk->dentry[bit_pos];
|
||||
de->hash_code = dentry_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);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
|
||||
f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos);
|
||||
|
||||
set_page_dirty(dentry_page);
|
||||
|
||||
/* 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);
|
||||
if (inode) {
|
||||
/* 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, current_depth);
|
||||
fail:
|
||||
up_write(&F2FS_I(inode)->i_sem);
|
||||
if (inode)
|
||||
up_write(&F2FS_I(inode)->i_sem);
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) {
|
||||
update_inode_page(dir);
|
||||
|
@ -536,6 +628,9 @@ fail:
|
|||
}
|
||||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
out:
|
||||
fscrypt_free_filename(&fname);
|
||||
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -545,7 +640,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
|
|||
int err = 0;
|
||||
|
||||
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)) {
|
||||
err = PTR_ERR(page);
|
||||
goto fail;
|
||||
|
@ -557,24 +652,58 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
|
|||
clear_inode_flag(F2FS_I(inode), FI_NEW_INODE);
|
||||
fail:
|
||||
up_write(&F2FS_I(inode)->i_sem);
|
||||
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
|
||||
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
|
||||
* entry in name page does not need to be touched during deletion.
|
||||
*/
|
||||
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;
|
||||
unsigned int bit_pos;
|
||||
struct inode *dir = page->mapping->host;
|
||||
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
|
||||
int i;
|
||||
|
||||
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
|
||||
|
||||
if (f2fs_has_inline_dentry(dir))
|
||||
return f2fs_delete_inline_entry(dentry, page, dir, inode);
|
||||
|
||||
lock_page(page);
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
|
||||
dentry_blk = page_address(page);
|
||||
bit_pos = dentry - dentry_blk->dentry;
|
||||
|
@ -590,33 +719,13 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
|||
|
||||
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
||||
|
||||
if (inode) {
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
if (inode)
|
||||
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) {
|
||||
truncate_hole(dir, page->index, page->index + 1);
|
||||
if (bit_pos == NR_DENTRY_IN_BLOCK &&
|
||||
!truncate_hole(dir, page->index, page->index + 1)) {
|
||||
clear_page_dirty_for_io(page);
|
||||
ClearPagePrivate(page);
|
||||
ClearPageUptodate(page);
|
||||
inode_dec_dirty_pages(dir);
|
||||
}
|
||||
|
@ -628,11 +737,14 @@ bool f2fs_empty_dir(struct inode *dir)
|
|||
unsigned long bidx;
|
||||
struct page *dentry_page;
|
||||
unsigned int bit_pos;
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
unsigned long nblock = dir_blocks(dir);
|
||||
|
||||
if (f2fs_has_inline_dentry(dir))
|
||||
return f2fs_empty_inline_dir(dir);
|
||||
|
||||
for (bidx = 0; bidx < nblock; bidx++) {
|
||||
dentry_page = get_lock_data_page(dir, bidx);
|
||||
dentry_page = get_lock_data_page(dir, bidx, false);
|
||||
if (IS_ERR(dentry_page)) {
|
||||
if (PTR_ERR(dentry_page) == -ENOENT)
|
||||
continue;
|
||||
|
@ -640,7 +752,6 @@ bool f2fs_empty_dir(struct inode *dir)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
dentry_blk = kmap_atomic(dentry_page);
|
||||
if (bidx == 0)
|
||||
bit_pos = 2;
|
||||
|
@ -659,19 +770,93 @@ bool f2fs_empty_dir(struct inode *dir)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
unsigned int start_pos, struct fscrypt_str *fstr)
|
||||
{
|
||||
unsigned char d_type = DT_UNKNOWN;
|
||||
unsigned int bit_pos;
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
struct fscrypt_str de_name = FSTR_INIT(NULL, 0);
|
||||
|
||||
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->name_len == 0) {
|
||||
bit_pos++;
|
||||
ctx->pos = start_pos + bit_pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (de->file_type < F2FS_FT_MAX)
|
||||
d_type = f2fs_filetype_table[de->file_type];
|
||||
else
|
||||
d_type = DT_UNKNOWN;
|
||||
|
||||
de_name.name = d->filename[bit_pos];
|
||||
de_name.len = le16_to_cpu(de->name_len);
|
||||
|
||||
if (f2fs_encrypted_inode(d->inode)) {
|
||||
int save_len = fstr->len;
|
||||
int ret;
|
||||
|
||||
de_name.name = kmalloc(de_name.len, GFP_NOFS);
|
||||
if (!de_name.name)
|
||||
return false;
|
||||
|
||||
memcpy(de_name.name, d->filename[bit_pos], de_name.len);
|
||||
|
||||
ret = fscrypt_fname_disk_to_usr(d->inode,
|
||||
(u32)de->hash_code, 0,
|
||||
&de_name, fstr);
|
||||
kfree(de_name.name);
|
||||
if (ret < 0)
|
||||
return true;
|
||||
|
||||
de_name = *fstr;
|
||||
fstr->len = save_len;
|
||||
}
|
||||
|
||||
if (!dir_emit(ctx, de_name.name, 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)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
unsigned long npages = dir_blocks(inode);
|
||||
unsigned int bit_pos = 0;
|
||||
struct f2fs_dentry_block *dentry_blk = NULL;
|
||||
struct f2fs_dir_entry *de = NULL;
|
||||
struct page *dentry_page = NULL;
|
||||
struct file_ra_state *ra = &file->f_ra;
|
||||
unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
|
||||
unsigned char d_type = DT_UNKNOWN;
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
|
||||
int err = 0;
|
||||
|
||||
bit_pos = ((unsigned long)ctx->pos % NR_DENTRY_IN_BLOCK);
|
||||
if (f2fs_encrypted_inode(inode)) {
|
||||
err = fscrypt_get_encryption_info(inode);
|
||||
if (err && err != -ENOKEY)
|
||||
return err;
|
||||
|
||||
err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (f2fs_has_inline_dentry(inode)) {
|
||||
err = f2fs_read_inline_dir(file, ctx, &fstr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* readahead for multi pages of dir */
|
||||
if (npages - n > 1 && !ra_has_index(ra, n))
|
||||
|
@ -679,44 +864,38 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
|
|||
min(npages - n, (pgoff_t)MAX_DIR_RA_PAGES));
|
||||
|
||||
for (; n < npages; n++) {
|
||||
dentry_page = get_lock_data_page(inode, n);
|
||||
if (IS_ERR(dentry_page))
|
||||
continue;
|
||||
dentry_page = get_lock_data_page(inode, n, false);
|
||||
if (IS_ERR(dentry_page)) {
|
||||
err = PTR_ERR(dentry_page);
|
||||
if (err == -ENOENT)
|
||||
continue;
|
||||
else
|
||||
goto out;
|
||||
}
|
||||
|
||||
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];
|
||||
if (de->file_type < F2FS_FT_MAX)
|
||||
d_type = f2fs_filetype_table[de->file_type];
|
||||
else
|
||||
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;
|
||||
make_dentry_ptr(inode, &d, (void *)dentry_blk, 1);
|
||||
|
||||
bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
|
||||
ctx->pos = n * NR_DENTRY_IN_BLOCK + bit_pos;
|
||||
if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) {
|
||||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
break;
|
||||
}
|
||||
bit_pos = 0;
|
||||
|
||||
ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK;
|
||||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
dentry_page = NULL;
|
||||
}
|
||||
stop:
|
||||
if (dentry_page && !IS_ERR(dentry_page)) {
|
||||
kunmap(dentry_page);
|
||||
f2fs_put_page(dentry_page, 1);
|
||||
}
|
||||
out:
|
||||
fscrypt_fname_free_buffer(&fstr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_dir_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
if (f2fs_encrypted_inode(inode))
|
||||
return fscrypt_get_encryption_info(inode) ? -EACCES : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -725,5 +904,9 @@ const struct file_operations f2fs_dir_operations = {
|
|||
.read = generic_read_dir,
|
||||
.iterate = f2fs_readdir,
|
||||
.fsync = f2fs_sync_file,
|
||||
.open = f2fs_dir_open,
|
||||
.unlocked_ioctl = f2fs_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = f2fs_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
|
736
fs/f2fs/extent_cache.c
Normal file
736
fs/f2fs/extent_cache.c
Normal file
|
@ -0,0 +1,736 @@
|
|||
/*
|
||||
* f2fs extent cache support
|
||||
*
|
||||
* Copyright (c) 2015 Motorola Mobility
|
||||
* Copyright (c) 2015 Samsung Electronics
|
||||
* Authors: Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
* Chao Yu <chao2.yu@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
static struct kmem_cache *extent_tree_slab;
|
||||
static struct kmem_cache *extent_node_slab;
|
||||
|
||||
static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_info *ei,
|
||||
struct rb_node *parent, struct rb_node **p)
|
||||
{
|
||||
struct extent_node *en;
|
||||
|
||||
en = kmem_cache_alloc(extent_node_slab, GFP_ATOMIC);
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
en->ei = *ei;
|
||||
INIT_LIST_HEAD(&en->list);
|
||||
en->et = et;
|
||||
|
||||
rb_link_node(&en->rb_node, parent, p);
|
||||
rb_insert_color(&en->rb_node, &et->root);
|
||||
atomic_inc(&et->node_cnt);
|
||||
atomic_inc(&sbi->total_ext_node);
|
||||
return en;
|
||||
}
|
||||
|
||||
static void __detach_extent_node(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_node *en)
|
||||
{
|
||||
rb_erase(&en->rb_node, &et->root);
|
||||
atomic_dec(&et->node_cnt);
|
||||
atomic_dec(&sbi->total_ext_node);
|
||||
|
||||
if (et->cached_en == en)
|
||||
et->cached_en = NULL;
|
||||
kmem_cache_free(extent_node_slab, en);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flow to release an extent_node:
|
||||
* 1. list_del_init
|
||||
* 2. __detach_extent_node
|
||||
* 3. kmem_cache_free.
|
||||
*/
|
||||
static void __release_extent_node(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_node *en)
|
||||
{
|
||||
spin_lock(&sbi->extent_lock);
|
||||
f2fs_bug_on(sbi, list_empty(&en->list));
|
||||
list_del_init(&en->list);
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
__detach_extent_node(sbi, et, en);
|
||||
}
|
||||
|
||||
static struct extent_tree *__grab_extent_tree(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et;
|
||||
nid_t ino = inode->i_ino;
|
||||
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
et = radix_tree_lookup(&sbi->extent_tree_root, ino);
|
||||
if (!et) {
|
||||
et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS);
|
||||
f2fs_radix_tree_insert(&sbi->extent_tree_root, ino, et);
|
||||
memset(et, 0, sizeof(struct extent_tree));
|
||||
et->ino = ino;
|
||||
et->root = RB_ROOT;
|
||||
et->cached_en = NULL;
|
||||
rwlock_init(&et->lock);
|
||||
INIT_LIST_HEAD(&et->list);
|
||||
atomic_set(&et->node_cnt, 0);
|
||||
atomic_inc(&sbi->total_ext_tree);
|
||||
} else {
|
||||
atomic_dec(&sbi->total_zombie_tree);
|
||||
list_del_init(&et->list);
|
||||
}
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
|
||||
/* never died until evict_inode */
|
||||
F2FS_I(inode)->extent_tree = et;
|
||||
|
||||
return et;
|
||||
}
|
||||
|
||||
static struct extent_node *__lookup_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, unsigned int fofs)
|
||||
{
|
||||
struct rb_node *node = et->root.rb_node;
|
||||
struct extent_node *en = et->cached_en;
|
||||
|
||||
if (en) {
|
||||
struct extent_info *cei = &en->ei;
|
||||
|
||||
if (cei->fofs <= fofs && cei->fofs + cei->len > fofs) {
|
||||
stat_inc_cached_node_hit(sbi);
|
||||
return en;
|
||||
}
|
||||
}
|
||||
|
||||
while (node) {
|
||||
en = rb_entry(node, struct extent_node, rb_node);
|
||||
|
||||
if (fofs < en->ei.fofs) {
|
||||
node = node->rb_left;
|
||||
} else if (fofs >= en->ei.fofs + en->ei.len) {
|
||||
node = node->rb_right;
|
||||
} else {
|
||||
stat_inc_rbtree_node_hit(sbi);
|
||||
return en;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct extent_node *__init_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_info *ei)
|
||||
{
|
||||
struct rb_node **p = &et->root.rb_node;
|
||||
struct extent_node *en;
|
||||
|
||||
en = __attach_extent_node(sbi, et, ei, NULL, p);
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
et->largest = en->ei;
|
||||
et->cached_en = en;
|
||||
return en;
|
||||
}
|
||||
|
||||
static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et)
|
||||
{
|
||||
struct rb_node *node, *next;
|
||||
struct extent_node *en;
|
||||
unsigned int count = atomic_read(&et->node_cnt);
|
||||
|
||||
node = rb_first(&et->root);
|
||||
while (node) {
|
||||
next = rb_next(node);
|
||||
en = rb_entry(node, struct extent_node, rb_node);
|
||||
__release_extent_node(sbi, et, en);
|
||||
node = next;
|
||||
}
|
||||
|
||||
return count - atomic_read(&et->node_cnt);
|
||||
}
|
||||
|
||||
static void __drop_largest_extent(struct inode *inode,
|
||||
pgoff_t fofs, unsigned int len)
|
||||
{
|
||||
struct extent_info *largest = &F2FS_I(inode)->extent_tree->largest;
|
||||
|
||||
if (fofs < largest->fofs + largest->len && fofs + len > largest->fofs)
|
||||
largest->len = 0;
|
||||
}
|
||||
|
||||
/* return true, if inode page is changed */
|
||||
bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et;
|
||||
struct extent_node *en;
|
||||
struct extent_info ei;
|
||||
|
||||
if (!f2fs_may_extent_tree(inode)) {
|
||||
/* drop largest extent */
|
||||
if (i_ext && i_ext->len) {
|
||||
i_ext->len = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
et = __grab_extent_tree(inode);
|
||||
|
||||
if (!i_ext || !i_ext->len)
|
||||
return false;
|
||||
|
||||
set_extent_info(&ei, le32_to_cpu(i_ext->fofs),
|
||||
le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len));
|
||||
|
||||
write_lock(&et->lock);
|
||||
if (atomic_read(&et->node_cnt))
|
||||
goto out;
|
||||
|
||||
en = __init_extent_tree(sbi, et, &ei);
|
||||
if (en) {
|
||||
spin_lock(&sbi->extent_lock);
|
||||
list_add_tail(&en->list, &sbi->extent_list);
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
}
|
||||
out:
|
||||
write_unlock(&et->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
|
||||
struct extent_info *ei)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
struct extent_node *en;
|
||||
bool ret = false;
|
||||
|
||||
f2fs_bug_on(sbi, !et);
|
||||
|
||||
trace_f2fs_lookup_extent_tree_start(inode, pgofs);
|
||||
|
||||
read_lock(&et->lock);
|
||||
|
||||
if (et->largest.fofs <= pgofs &&
|
||||
et->largest.fofs + et->largest.len > pgofs) {
|
||||
*ei = et->largest;
|
||||
ret = true;
|
||||
stat_inc_largest_node_hit(sbi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
en = __lookup_extent_tree(sbi, et, pgofs);
|
||||
if (en) {
|
||||
*ei = en->ei;
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list)) {
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
ret = true;
|
||||
}
|
||||
out:
|
||||
stat_inc_total_hit(sbi);
|
||||
read_unlock(&et->lock);
|
||||
|
||||
trace_f2fs_lookup_extent_tree_end(inode, pgofs, ei);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* lookup extent at @fofs, if hit, return the extent
|
||||
* if not, return NULL and
|
||||
* @prev_ex: extent before fofs
|
||||
* @next_ex: extent after fofs
|
||||
* @insert_p: insert point for new extent at fofs
|
||||
* in order to simpfy the insertion after.
|
||||
* tree must stay unchanged between lookup and insertion.
|
||||
*/
|
||||
static struct extent_node *__lookup_extent_tree_ret(struct extent_tree *et,
|
||||
unsigned int fofs,
|
||||
struct extent_node **prev_ex,
|
||||
struct extent_node **next_ex,
|
||||
struct rb_node ***insert_p,
|
||||
struct rb_node **insert_parent)
|
||||
{
|
||||
struct rb_node **pnode = &et->root.rb_node;
|
||||
struct rb_node *parent = NULL, *tmp_node;
|
||||
struct extent_node *en = et->cached_en;
|
||||
|
||||
*insert_p = NULL;
|
||||
*insert_parent = NULL;
|
||||
*prev_ex = NULL;
|
||||
*next_ex = NULL;
|
||||
|
||||
if (RB_EMPTY_ROOT(&et->root))
|
||||
return NULL;
|
||||
|
||||
if (en) {
|
||||
struct extent_info *cei = &en->ei;
|
||||
|
||||
if (cei->fofs <= fofs && cei->fofs + cei->len > fofs)
|
||||
goto lookup_neighbors;
|
||||
}
|
||||
|
||||
while (*pnode) {
|
||||
parent = *pnode;
|
||||
en = rb_entry(*pnode, struct extent_node, rb_node);
|
||||
|
||||
if (fofs < en->ei.fofs)
|
||||
pnode = &(*pnode)->rb_left;
|
||||
else if (fofs >= en->ei.fofs + en->ei.len)
|
||||
pnode = &(*pnode)->rb_right;
|
||||
else
|
||||
goto lookup_neighbors;
|
||||
}
|
||||
|
||||
*insert_p = pnode;
|
||||
*insert_parent = parent;
|
||||
|
||||
en = rb_entry(parent, struct extent_node, rb_node);
|
||||
tmp_node = parent;
|
||||
if (parent && fofs > en->ei.fofs)
|
||||
tmp_node = rb_next(parent);
|
||||
*next_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
|
||||
tmp_node = parent;
|
||||
if (parent && fofs < en->ei.fofs)
|
||||
tmp_node = rb_prev(parent);
|
||||
*prev_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
return NULL;
|
||||
|
||||
lookup_neighbors:
|
||||
if (fofs == en->ei.fofs) {
|
||||
/* lookup prev node for merging backward later */
|
||||
tmp_node = rb_prev(&en->rb_node);
|
||||
*prev_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
}
|
||||
if (fofs == en->ei.fofs + en->ei.len - 1) {
|
||||
/* lookup next node for merging frontward later */
|
||||
tmp_node = rb_next(&en->rb_node);
|
||||
*next_ex = tmp_node ?
|
||||
rb_entry(tmp_node, struct extent_node, rb_node) : NULL;
|
||||
}
|
||||
return en;
|
||||
}
|
||||
|
||||
static struct extent_node *__try_merge_extent_node(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_info *ei,
|
||||
struct extent_node *prev_ex,
|
||||
struct extent_node *next_ex)
|
||||
{
|
||||
struct extent_node *en = NULL;
|
||||
|
||||
if (prev_ex && __is_back_mergeable(ei, &prev_ex->ei)) {
|
||||
prev_ex->ei.len += ei->len;
|
||||
ei = &prev_ex->ei;
|
||||
en = prev_ex;
|
||||
}
|
||||
|
||||
if (next_ex && __is_front_mergeable(ei, &next_ex->ei)) {
|
||||
if (en)
|
||||
__release_extent_node(sbi, et, prev_ex);
|
||||
next_ex->ei.fofs = ei->fofs;
|
||||
next_ex->ei.blk = ei->blk;
|
||||
next_ex->ei.len += ei->len;
|
||||
en = next_ex;
|
||||
}
|
||||
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
__try_update_largest_extent(et, en);
|
||||
|
||||
spin_lock(&sbi->extent_lock);
|
||||
if (!list_empty(&en->list)) {
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
return en;
|
||||
}
|
||||
|
||||
static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi,
|
||||
struct extent_tree *et, struct extent_info *ei,
|
||||
struct rb_node **insert_p,
|
||||
struct rb_node *insert_parent)
|
||||
{
|
||||
struct rb_node **p = &et->root.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct extent_node *en = NULL;
|
||||
|
||||
if (insert_p && insert_parent) {
|
||||
parent = insert_parent;
|
||||
p = insert_p;
|
||||
goto do_insert;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
en = rb_entry(parent, struct extent_node, rb_node);
|
||||
|
||||
if (ei->fofs < en->ei.fofs)
|
||||
p = &(*p)->rb_left;
|
||||
else if (ei->fofs >= en->ei.fofs + en->ei.len)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
do_insert:
|
||||
en = __attach_extent_node(sbi, et, ei, parent, p);
|
||||
if (!en)
|
||||
return NULL;
|
||||
|
||||
__try_update_largest_extent(et, en);
|
||||
|
||||
/* update in global extent list */
|
||||
spin_lock(&sbi->extent_lock);
|
||||
list_add_tail(&en->list, &sbi->extent_list);
|
||||
et->cached_en = en;
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
return en;
|
||||
}
|
||||
|
||||
static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
|
||||
pgoff_t fofs, block_t blkaddr, unsigned int len)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
struct extent_node *en = NULL, *en1 = NULL;
|
||||
struct extent_node *prev_en = NULL, *next_en = NULL;
|
||||
struct extent_info ei, dei, prev;
|
||||
struct rb_node **insert_p = NULL, *insert_parent = NULL;
|
||||
unsigned int end = fofs + len;
|
||||
unsigned int pos = (unsigned int)fofs;
|
||||
|
||||
if (!et)
|
||||
return false;
|
||||
|
||||
trace_f2fs_update_extent_tree_range(inode, fofs, blkaddr, len);
|
||||
|
||||
write_lock(&et->lock);
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) {
|
||||
write_unlock(&et->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
prev = et->largest;
|
||||
dei.len = 0;
|
||||
|
||||
/*
|
||||
* drop largest extent before lookup, in case it's already
|
||||
* been shrunk from extent tree
|
||||
*/
|
||||
__drop_largest_extent(inode, fofs, len);
|
||||
|
||||
/* 1. lookup first extent node in range [fofs, fofs + len - 1] */
|
||||
en = __lookup_extent_tree_ret(et, fofs, &prev_en, &next_en,
|
||||
&insert_p, &insert_parent);
|
||||
if (!en)
|
||||
en = next_en;
|
||||
|
||||
/* 2. invlidate all extent nodes in range [fofs, fofs + len - 1] */
|
||||
while (en && en->ei.fofs < end) {
|
||||
unsigned int org_end;
|
||||
int parts = 0; /* # of parts current extent split into */
|
||||
|
||||
next_en = en1 = NULL;
|
||||
|
||||
dei = en->ei;
|
||||
org_end = dei.fofs + dei.len;
|
||||
f2fs_bug_on(sbi, pos >= org_end);
|
||||
|
||||
if (pos > dei.fofs && pos - dei.fofs >= F2FS_MIN_EXTENT_LEN) {
|
||||
en->ei.len = pos - en->ei.fofs;
|
||||
prev_en = en;
|
||||
parts = 1;
|
||||
}
|
||||
|
||||
if (end < org_end && org_end - end >= F2FS_MIN_EXTENT_LEN) {
|
||||
if (parts) {
|
||||
set_extent_info(&ei, end,
|
||||
end - dei.fofs + dei.blk,
|
||||
org_end - end);
|
||||
en1 = __insert_extent_tree(sbi, et, &ei,
|
||||
NULL, NULL);
|
||||
next_en = en1;
|
||||
} else {
|
||||
en->ei.fofs = end;
|
||||
en->ei.blk += end - dei.fofs;
|
||||
en->ei.len -= end - dei.fofs;
|
||||
next_en = en;
|
||||
}
|
||||
parts++;
|
||||
}
|
||||
|
||||
if (!next_en) {
|
||||
struct rb_node *node = rb_next(&en->rb_node);
|
||||
|
||||
next_en = node ?
|
||||
rb_entry(node, struct extent_node, rb_node)
|
||||
: NULL;
|
||||
}
|
||||
|
||||
if (parts)
|
||||
__try_update_largest_extent(et, en);
|
||||
else
|
||||
__release_extent_node(sbi, et, en);
|
||||
|
||||
/*
|
||||
* if original extent is split into zero or two parts, extent
|
||||
* tree has been altered by deletion or insertion, therefore
|
||||
* invalidate pointers regard to tree.
|
||||
*/
|
||||
if (parts != 1) {
|
||||
insert_p = NULL;
|
||||
insert_parent = NULL;
|
||||
}
|
||||
en = next_en;
|
||||
}
|
||||
|
||||
/* 3. update extent in extent cache */
|
||||
if (blkaddr) {
|
||||
|
||||
set_extent_info(&ei, fofs, blkaddr, len);
|
||||
if (!__try_merge_extent_node(sbi, et, &ei, prev_en, next_en))
|
||||
__insert_extent_tree(sbi, et, &ei,
|
||||
insert_p, insert_parent);
|
||||
|
||||
/* give up extent_cache, if split and small updates happen */
|
||||
if (dei.len >= 1 &&
|
||||
prev.len < F2FS_MIN_EXTENT_LEN &&
|
||||
et->largest.len < F2FS_MIN_EXTENT_LEN) {
|
||||
et->largest.len = 0;
|
||||
set_inode_flag(F2FS_I(inode), FI_NO_EXTENT);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
|
||||
__free_extent_tree(sbi, et);
|
||||
|
||||
write_unlock(&et->lock);
|
||||
|
||||
return !__is_extent_same(&prev, &et->largest);
|
||||
}
|
||||
|
||||
unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
{
|
||||
struct extent_tree *et, *next;
|
||||
struct extent_node *en;
|
||||
unsigned int node_cnt = 0, tree_cnt = 0;
|
||||
int remained;
|
||||
|
||||
if (!test_opt(sbi, EXTENT_CACHE))
|
||||
return 0;
|
||||
|
||||
if (!atomic_read(&sbi->total_zombie_tree))
|
||||
goto free_node;
|
||||
|
||||
if (!down_write_trylock(&sbi->extent_tree_lock))
|
||||
goto out;
|
||||
|
||||
/* 1. remove unreferenced extent tree */
|
||||
list_for_each_entry_safe(et, next, &sbi->zombie_list, list) {
|
||||
if (atomic_read(&et->node_cnt)) {
|
||||
write_lock(&et->lock);
|
||||
node_cnt += __free_extent_tree(sbi, et);
|
||||
write_unlock(&et->lock);
|
||||
}
|
||||
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
|
||||
list_del_init(&et->list);
|
||||
radix_tree_delete(&sbi->extent_tree_root, et->ino);
|
||||
kmem_cache_free(extent_tree_slab, et);
|
||||
atomic_dec(&sbi->total_ext_tree);
|
||||
atomic_dec(&sbi->total_zombie_tree);
|
||||
tree_cnt++;
|
||||
|
||||
if (node_cnt + tree_cnt >= nr_shrink)
|
||||
goto unlock_out;
|
||||
cond_resched();
|
||||
}
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
|
||||
free_node:
|
||||
/* 2. remove LRU extent entries */
|
||||
if (!down_write_trylock(&sbi->extent_tree_lock))
|
||||
goto out;
|
||||
|
||||
remained = nr_shrink - (node_cnt + tree_cnt);
|
||||
|
||||
spin_lock(&sbi->extent_lock);
|
||||
for (; remained > 0; remained--) {
|
||||
if (list_empty(&sbi->extent_list))
|
||||
break;
|
||||
en = list_first_entry(&sbi->extent_list,
|
||||
struct extent_node, list);
|
||||
et = en->et;
|
||||
if (!write_trylock(&et->lock)) {
|
||||
/* refresh this extent node's position in extent list */
|
||||
list_move_tail(&en->list, &sbi->extent_list);
|
||||
continue;
|
||||
}
|
||||
|
||||
list_del_init(&en->list);
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
__detach_extent_node(sbi, et, en);
|
||||
|
||||
write_unlock(&et->lock);
|
||||
node_cnt++;
|
||||
spin_lock(&sbi->extent_lock);
|
||||
}
|
||||
spin_unlock(&sbi->extent_lock);
|
||||
|
||||
unlock_out:
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
out:
|
||||
trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt);
|
||||
|
||||
return node_cnt + tree_cnt;
|
||||
}
|
||||
|
||||
unsigned int f2fs_destroy_extent_node(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
unsigned int node_cnt = 0;
|
||||
|
||||
if (!et || !atomic_read(&et->node_cnt))
|
||||
return 0;
|
||||
|
||||
write_lock(&et->lock);
|
||||
node_cnt = __free_extent_tree(sbi, et);
|
||||
write_unlock(&et->lock);
|
||||
|
||||
return node_cnt;
|
||||
}
|
||||
|
||||
void f2fs_destroy_extent_tree(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct extent_tree *et = F2FS_I(inode)->extent_tree;
|
||||
unsigned int node_cnt = 0;
|
||||
|
||||
if (!et)
|
||||
return;
|
||||
|
||||
if (inode->i_nlink && !is_bad_inode(inode) &&
|
||||
atomic_read(&et->node_cnt)) {
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
list_add_tail(&et->list, &sbi->zombie_list);
|
||||
atomic_inc(&sbi->total_zombie_tree);
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* free all extent info belong to this extent tree */
|
||||
node_cnt = f2fs_destroy_extent_node(inode);
|
||||
|
||||
/* delete extent tree entry in radix tree */
|
||||
down_write(&sbi->extent_tree_lock);
|
||||
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
|
||||
radix_tree_delete(&sbi->extent_tree_root, inode->i_ino);
|
||||
kmem_cache_free(extent_tree_slab, et);
|
||||
atomic_dec(&sbi->total_ext_tree);
|
||||
up_write(&sbi->extent_tree_lock);
|
||||
|
||||
F2FS_I(inode)->extent_tree = NULL;
|
||||
|
||||
trace_f2fs_destroy_extent_tree(inode, node_cnt);
|
||||
}
|
||||
|
||||
bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs,
|
||||
struct extent_info *ei)
|
||||
{
|
||||
if (!f2fs_may_extent_tree(inode))
|
||||
return false;
|
||||
|
||||
return f2fs_lookup_extent_tree(inode, pgofs, ei);
|
||||
}
|
||||
|
||||
void f2fs_update_extent_cache(struct dnode_of_data *dn)
|
||||
{
|
||||
pgoff_t fofs;
|
||||
block_t blkaddr;
|
||||
|
||||
if (!f2fs_may_extent_tree(dn->inode))
|
||||
return;
|
||||
|
||||
if (dn->data_blkaddr == NEW_ADDR)
|
||||
blkaddr = NULL_ADDR;
|
||||
else
|
||||
blkaddr = dn->data_blkaddr;
|
||||
|
||||
fofs = start_bidx_of_node(ofs_of_node(dn->node_page), dn->inode) +
|
||||
dn->ofs_in_node;
|
||||
|
||||
if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, 1))
|
||||
sync_inode_page(dn);
|
||||
}
|
||||
|
||||
void f2fs_update_extent_cache_range(struct dnode_of_data *dn,
|
||||
pgoff_t fofs, block_t blkaddr, unsigned int len)
|
||||
|
||||
{
|
||||
if (!f2fs_may_extent_tree(dn->inode))
|
||||
return;
|
||||
|
||||
if (f2fs_update_extent_tree_range(dn->inode, fofs, blkaddr, len))
|
||||
sync_inode_page(dn);
|
||||
}
|
||||
|
||||
void init_extent_cache_info(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO);
|
||||
init_rwsem(&sbi->extent_tree_lock);
|
||||
INIT_LIST_HEAD(&sbi->extent_list);
|
||||
spin_lock_init(&sbi->extent_lock);
|
||||
atomic_set(&sbi->total_ext_tree, 0);
|
||||
INIT_LIST_HEAD(&sbi->zombie_list);
|
||||
atomic_set(&sbi->total_zombie_tree, 0);
|
||||
atomic_set(&sbi->total_ext_node, 0);
|
||||
}
|
||||
|
||||
int __init create_extent_cache(void)
|
||||
{
|
||||
extent_tree_slab = f2fs_kmem_cache_create("f2fs_extent_tree",
|
||||
sizeof(struct extent_tree));
|
||||
if (!extent_tree_slab)
|
||||
return -ENOMEM;
|
||||
extent_node_slab = f2fs_kmem_cache_create("f2fs_extent_node",
|
||||
sizeof(struct extent_node));
|
||||
if (!extent_node_slab) {
|
||||
kmem_cache_destroy(extent_tree_slab);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_extent_cache(void)
|
||||
{
|
||||
kmem_cache_destroy(extent_node_slab);
|
||||
kmem_cache_destroy(extent_tree_slab);
|
||||
}
|
1066
fs/f2fs/f2fs.h
1066
fs/f2fs/f2fs.h
File diff suppressed because it is too large
Load diff
1282
fs/f2fs/file.c
1282
fs/f2fs/file.c
File diff suppressed because it is too large
Load diff
538
fs/f2fs/gc.c
538
fs/f2fs/gc.c
|
@ -16,7 +16,6 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
@ -24,8 +23,6 @@
|
|||
#include "gc.h"
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
static struct kmem_cache *winode_slab;
|
||||
|
||||
static int gc_thread_func(void *data)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = data;
|
||||
|
@ -46,7 +43,7 @@ static int gc_thread_func(void *data)
|
|||
break;
|
||||
|
||||
if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) {
|
||||
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -67,22 +64,25 @@ static int gc_thread_func(void *data)
|
|||
continue;
|
||||
|
||||
if (!is_idle(sbi)) {
|
||||
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
mutex_unlock(&sbi->gc_mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (has_enough_invalid_blocks(sbi))
|
||||
wait_ms = decrease_sleep_time(gc_th, wait_ms);
|
||||
decrease_sleep_time(gc_th, &wait_ms);
|
||||
else
|
||||
wait_ms = increase_sleep_time(gc_th, wait_ms);
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
|
||||
stat_inc_bggc_count(sbi);
|
||||
|
||||
/* if return value is not zero, no victim was selected */
|
||||
if (f2fs_gc(sbi))
|
||||
if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC)))
|
||||
wait_ms = gc_th->no_gc_sleep_time;
|
||||
|
||||
trace_f2fs_background_gc(sbi->sb, wait_ms,
|
||||
prefree_segments(sbi), free_segments(sbi));
|
||||
|
||||
/* balancing f2fs's metadata periodically */
|
||||
f2fs_balance_fs_bg(sbi);
|
||||
|
||||
|
@ -96,8 +96,6 @@ int start_gc_thread(struct f2fs_sb_info *sbi)
|
|||
dev_t dev = sbi->sb->s_bdev->bd_dev;
|
||||
int err = 0;
|
||||
|
||||
if (!test_opt(sbi, BG_GC))
|
||||
goto out;
|
||||
gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL);
|
||||
if (!gc_th) {
|
||||
err = -ENOMEM;
|
||||
|
@ -174,9 +172,9 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
/* SSR allocates in a segment unit */
|
||||
if (p->alloc_mode == SSR)
|
||||
return 1 << sbi->log_blocks_per_seg;
|
||||
return sbi->blocks_per_seg;
|
||||
if (p->gc_mode == GC_GREEDY)
|
||||
return (1 << sbi->log_blocks_per_seg) * p->ofs_unit;
|
||||
return sbi->blocks_per_seg * p->ofs_unit;
|
||||
else if (p->gc_mode == GC_CB)
|
||||
return UINT_MAX;
|
||||
else /* No other gc_mode */
|
||||
|
@ -247,6 +245,18 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi,
|
|||
return get_cb_cost(sbi, segno);
|
||||
}
|
||||
|
||||
static unsigned int count_bits(const unsigned long *addr,
|
||||
unsigned int offset, unsigned int len)
|
||||
{
|
||||
unsigned int end = offset + len, sum = 0;
|
||||
|
||||
while (offset < end) {
|
||||
if (test_bit(offset++, addr))
|
||||
++sum;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called from two paths.
|
||||
* One is garbage collection and the other is SSR segment selection.
|
||||
|
@ -260,8 +270,9 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||
struct victim_sel_policy p;
|
||||
unsigned int secno, max_cost;
|
||||
int nsearched = 0;
|
||||
unsigned int secno, max_cost, last_victim;
|
||||
unsigned int last_segment = MAIN_SEGS(sbi);
|
||||
unsigned int nsearched = 0;
|
||||
|
||||
mutex_lock(&dirty_i->seglist_lock);
|
||||
|
||||
|
@ -271,6 +282,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
p.min_segno = NULL_SEGNO;
|
||||
p.min_cost = max_cost = get_max_cost(sbi, &p);
|
||||
|
||||
if (p.max_search == 0)
|
||||
goto out;
|
||||
|
||||
last_victim = sbi->last_victim[p.gc_mode];
|
||||
if (p.alloc_mode == LFS && gc_type == FG_GC) {
|
||||
p.min_segno = check_bg_victims(sbi);
|
||||
if (p.min_segno != NULL_SEGNO)
|
||||
|
@ -281,9 +296,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
unsigned long cost;
|
||||
unsigned int segno;
|
||||
|
||||
segno = find_next_bit(p.dirty_segmap, MAIN_SEGS(sbi), p.offset);
|
||||
if (segno >= MAIN_SEGS(sbi)) {
|
||||
segno = find_next_bit(p.dirty_segmap, last_segment, p.offset);
|
||||
if (segno >= last_segment) {
|
||||
if (sbi->last_victim[p.gc_mode]) {
|
||||
last_segment = sbi->last_victim[p.gc_mode];
|
||||
sbi->last_victim[p.gc_mode] = 0;
|
||||
p.offset = 0;
|
||||
continue;
|
||||
|
@ -292,27 +308,35 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||
}
|
||||
|
||||
p.offset = segno + p.ofs_unit;
|
||||
if (p.ofs_unit > 1)
|
||||
if (p.ofs_unit > 1) {
|
||||
p.offset -= segno % p.ofs_unit;
|
||||
nsearched += count_bits(p.dirty_segmap,
|
||||
p.offset - p.ofs_unit,
|
||||
p.ofs_unit);
|
||||
} else {
|
||||
nsearched++;
|
||||
}
|
||||
|
||||
|
||||
secno = GET_SECNO(sbi, segno);
|
||||
|
||||
if (sec_usage_check(sbi, secno))
|
||||
continue;
|
||||
goto next;
|
||||
if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
|
||||
continue;
|
||||
goto next;
|
||||
|
||||
cost = get_gc_cost(sbi, segno, &p);
|
||||
|
||||
if (p.min_cost > cost) {
|
||||
p.min_segno = segno;
|
||||
p.min_cost = cost;
|
||||
} else if (unlikely(cost == max_cost)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nsearched++ >= p.max_search) {
|
||||
sbi->last_victim[p.gc_mode] = segno;
|
||||
next:
|
||||
if (nsearched >= p.max_search) {
|
||||
if (!sbi->last_victim[p.gc_mode] && segno <= last_victim)
|
||||
sbi->last_victim[p.gc_mode] = last_victim + 1;
|
||||
else
|
||||
sbi->last_victim[p.gc_mode] = segno + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -331,6 +355,7 @@ got_it:
|
|||
sbi->cur_victim_sec,
|
||||
prefree_segments(sbi), free_segments(sbi));
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&dirty_i->seglist_lock);
|
||||
|
||||
return (p.min_segno == NULL_SEGNO) ? 0 : 1;
|
||||
|
@ -340,37 +365,39 @@ static const struct victim_selection default_v_ops = {
|
|||
.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;
|
||||
|
||||
list_for_each_entry(ie, ilist, list)
|
||||
if (ie->inode->i_ino == ino)
|
||||
return ie->inode;
|
||||
ie = radix_tree_lookup(&gc_list->iroot, ino);
|
||||
if (ie)
|
||||
return ie->inode;
|
||||
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;
|
||||
|
||||
if (inode == find_gc_inode(inode->i_ino, ilist)) {
|
||||
if (inode == find_gc_inode(gc_list, inode->i_ino)) {
|
||||
iput(inode);
|
||||
return;
|
||||
}
|
||||
|
||||
new_ie = f2fs_kmem_cache_alloc(winode_slab, GFP_NOFS);
|
||||
new_ie = f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS);
|
||||
new_ie->inode = inode;
|
||||
list_add_tail(&new_ie->list, ilist);
|
||||
|
||||
f2fs_radix_tree_insert(&gc_list->iroot, inode->i_ino, new_ie);
|
||||
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;
|
||||
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);
|
||||
list_del(&ie->list);
|
||||
kmem_cache_free(winode_slab, ie);
|
||||
kmem_cache_free(inode_entry_slab, ie);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,14 +425,18 @@ static void gc_node_segment(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
bool initial = true;
|
||||
struct f2fs_summary *entry;
|
||||
block_t start_addr;
|
||||
int off;
|
||||
|
||||
start_addr = START_BLOCK(sbi, segno);
|
||||
|
||||
next_step:
|
||||
entry = sum;
|
||||
|
||||
for (off = 0; off < sbi->blocks_per_seg; off++, entry++) {
|
||||
nid_t nid = le32_to_cpu(entry->nid);
|
||||
struct page *node_page;
|
||||
struct node_info ni;
|
||||
|
||||
/* stop BG_GC if there is not enough free sections. */
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
|
||||
|
@ -428,38 +459,28 @@ next_step:
|
|||
continue;
|
||||
}
|
||||
|
||||
get_node_info(sbi, nid, &ni);
|
||||
if (ni.blk_addr != start_addr + off) {
|
||||
f2fs_put_page(node_page, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set page dirty and write it */
|
||||
if (gc_type == FG_GC) {
|
||||
f2fs_wait_on_page_writeback(node_page, NODE);
|
||||
f2fs_wait_on_page_writeback(node_page, NODE, true);
|
||||
set_page_dirty(node_page);
|
||||
} else {
|
||||
if (!PageWriteback(node_page))
|
||||
set_page_dirty(node_page);
|
||||
}
|
||||
f2fs_put_page(node_page, 1);
|
||||
stat_inc_node_blk_count(sbi, 1);
|
||||
stat_inc_node_blk_count(sbi, 1, gc_type);
|
||||
}
|
||||
|
||||
if (initial) {
|
||||
initial = false;
|
||||
goto next_step;
|
||||
}
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
struct writeback_control wbc = {
|
||||
.sync_mode = WB_SYNC_ALL,
|
||||
.nr_to_write = LONG_MAX,
|
||||
.for_reclaim = 0,
|
||||
};
|
||||
sync_node_pages(sbi, 0, &wbc);
|
||||
|
||||
/*
|
||||
* In the case of FG_GC, it'd be better to reclaim this victim
|
||||
* completely.
|
||||
*/
|
||||
if (get_valid_blocks(sbi, segno, 1) != 0)
|
||||
goto next_step;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -469,7 +490,7 @@ next_step:
|
|||
* as indirect or double indirect node blocks, are given, it must be a caller's
|
||||
* bug.
|
||||
*/
|
||||
block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi)
|
||||
block_t start_bidx_of_node(unsigned int node_ofs, struct inode *inode)
|
||||
{
|
||||
unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4;
|
||||
unsigned int bidx;
|
||||
|
@ -486,10 +507,10 @@ block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi)
|
|||
int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1);
|
||||
bidx = node_ofs - 5 - dec;
|
||||
}
|
||||
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi);
|
||||
return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode);
|
||||
}
|
||||
|
||||
static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
struct node_info *dni, block_t blkaddr, unsigned int *nofs)
|
||||
{
|
||||
struct page *node_page;
|
||||
|
@ -502,13 +523,13 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|||
|
||||
node_page = get_node_page(sbi, nid);
|
||||
if (IS_ERR(node_page))
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
get_node_info(sbi, nid, dni);
|
||||
|
||||
if (sum->version != dni->version) {
|
||||
f2fs_put_page(node_page, 1);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
*nofs = ofs_of_node(node_page);
|
||||
|
@ -516,16 +537,116 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|||
f2fs_put_page(node_page, 1);
|
||||
|
||||
if (source_blkaddr != blkaddr)
|
||||
return 0;
|
||||
return 1;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void move_data_page(struct inode *inode, struct page *page, int gc_type)
|
||||
static void move_encrypted_block(struct inode *inode, block_t bidx)
|
||||
{
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC,
|
||||
.rw = READ_SYNC,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
struct page *page;
|
||||
block_t newaddr;
|
||||
int err;
|
||||
|
||||
/* do not read out */
|
||||
page = f2fs_grab_cache_page(inode->i_mapping, bidx, false);
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (unlikely(dn.data_blkaddr == NULL_ADDR)) {
|
||||
ClearPageUptodate(page);
|
||||
goto put_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* don't cache encrypted data into meta inode until previous dirty
|
||||
* data were writebacked to avoid racing between GC and flush.
|
||||
*/
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
|
||||
get_node_info(fio.sbi, dn.nid, &ni);
|
||||
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
||||
|
||||
/* read page */
|
||||
fio.page = page;
|
||||
fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
|
||||
|
||||
allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
|
||||
&sum, CURSEG_COLD_DATA);
|
||||
|
||||
fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr,
|
||||
FGP_LOCK | FGP_CREAT, GFP_NOFS);
|
||||
if (!fio.encrypted_page) {
|
||||
err = -ENOMEM;
|
||||
goto recover_block;
|
||||
}
|
||||
|
||||
err = f2fs_submit_page_bio(&fio);
|
||||
if (err)
|
||||
goto put_page_out;
|
||||
|
||||
/* write page */
|
||||
lock_page(fio.encrypted_page);
|
||||
|
||||
if (unlikely(!PageUptodate(fio.encrypted_page))) {
|
||||
err = -EIO;
|
||||
goto put_page_out;
|
||||
}
|
||||
if (unlikely(fio.encrypted_page->mapping != META_MAPPING(fio.sbi))) {
|
||||
err = -EIO;
|
||||
goto put_page_out;
|
||||
}
|
||||
|
||||
set_page_dirty(fio.encrypted_page);
|
||||
f2fs_wait_on_page_writeback(fio.encrypted_page, DATA, true);
|
||||
if (clear_page_dirty_for_io(fio.encrypted_page))
|
||||
dec_page_count(fio.sbi, F2FS_DIRTY_META);
|
||||
|
||||
set_page_writeback(fio.encrypted_page);
|
||||
|
||||
/* allocate block address */
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
|
||||
|
||||
fio.rw = WRITE_SYNC;
|
||||
fio.new_blkaddr = newaddr;
|
||||
f2fs_submit_page_mbio(&fio);
|
||||
|
||||
f2fs_update_data_blkaddr(&dn, newaddr);
|
||||
set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
|
||||
if (page->index == 0)
|
||||
set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
|
||||
put_page_out:
|
||||
f2fs_put_page(fio.encrypted_page, 1);
|
||||
recover_block:
|
||||
if (err)
|
||||
__f2fs_replace_block(fio.sbi, &sum, newaddr, fio.old_blkaddr,
|
||||
true, true);
|
||||
put_out:
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = get_lock_data_page(inode, bidx, true);
|
||||
if (IS_ERR(page))
|
||||
return;
|
||||
|
||||
if (gc_type == BG_GC) {
|
||||
if (PageWriteback(page))
|
||||
|
@ -533,12 +654,19 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type)
|
|||
set_page_dirty(page);
|
||||
set_cold_data(page);
|
||||
} else {
|
||||
f2fs_wait_on_page_writeback(page, DATA);
|
||||
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
set_page_dirty(page);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
if (clear_page_dirty_for_io(page))
|
||||
inode_dec_dirty_pages(inode);
|
||||
set_cold_data(page);
|
||||
do_write_data_page(page, &fio);
|
||||
do_write_data_page(&fio);
|
||||
clear_cold_data(page);
|
||||
}
|
||||
out:
|
||||
|
@ -553,7 +681,7 @@ out:
|
|||
* the victim data block is ignored.
|
||||
*/
|
||||
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 f2fs_summary *entry;
|
||||
|
@ -586,7 +714,7 @@ next_step:
|
|||
}
|
||||
|
||||
/* Get an inode by ino with checking validity */
|
||||
if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0)
|
||||
if (!is_alive(sbi, entry, &dni, start_addr + off, &nofs))
|
||||
continue;
|
||||
|
||||
if (phase == 1) {
|
||||
|
@ -601,141 +729,199 @@ next_step:
|
|||
if (IS_ERR(inode) || is_bad_inode(inode))
|
||||
continue;
|
||||
|
||||
start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
|
||||
/* if encrypted inode, let's go phase 3 */
|
||||
if (f2fs_encrypted_inode(inode) &&
|
||||
S_ISREG(inode->i_mode)) {
|
||||
add_gc_inode(gc_list, inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
data_page = find_data_page(inode,
|
||||
start_bidx + ofs_in_node, false);
|
||||
if (IS_ERR(data_page))
|
||||
goto next_iput;
|
||||
start_bidx = start_bidx_of_node(nofs, inode);
|
||||
data_page = get_read_data_page(inode,
|
||||
start_bidx + ofs_in_node, READA, true);
|
||||
if (IS_ERR(data_page)) {
|
||||
iput(inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
f2fs_put_page(data_page, 0);
|
||||
add_gc_inode(inode, ilist);
|
||||
} else {
|
||||
inode = find_gc_inode(dni.ino, ilist);
|
||||
if (inode) {
|
||||
start_bidx = start_bidx_of_node(nofs,
|
||||
F2FS_I(inode));
|
||||
data_page = get_lock_data_page(inode,
|
||||
start_bidx + ofs_in_node);
|
||||
if (IS_ERR(data_page))
|
||||
continue;
|
||||
move_data_page(inode, data_page, gc_type);
|
||||
stat_inc_data_blk_count(sbi, 1);
|
||||
}
|
||||
add_gc_inode(gc_list, inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* phase 3 */
|
||||
inode = find_gc_inode(gc_list, dni.ino);
|
||||
if (inode) {
|
||||
start_bidx = start_bidx_of_node(nofs, inode)
|
||||
+ ofs_in_node;
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
move_encrypted_block(inode, start_bidx);
|
||||
else
|
||||
move_data_page(inode, start_bidx, gc_type);
|
||||
stat_inc_data_blk_count(sbi, 1, gc_type);
|
||||
}
|
||||
continue;
|
||||
next_iput:
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
if (++phase < 4)
|
||||
goto next_step;
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
|
||||
/*
|
||||
* In the case of FG_GC, it'd be better to reclaim this victim
|
||||
* completely.
|
||||
*/
|
||||
if (get_valid_blocks(sbi, segno, 1) != 0) {
|
||||
phase = 2;
|
||||
goto next_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
int ret;
|
||||
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
|
||||
struct list_head *ilist, int gc_type)
|
||||
static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
unsigned int start_segno,
|
||||
struct gc_inode_list *gc_list, int gc_type)
|
||||
{
|
||||
struct page *sum_page;
|
||||
struct f2fs_summary_block *sum;
|
||||
struct blk_plug plug;
|
||||
|
||||
/* read segment summary of victim */
|
||||
sum_page = get_sum_page(sbi, segno);
|
||||
|
||||
blk_start_plug(&plug);
|
||||
|
||||
sum = page_address(sum_page);
|
||||
|
||||
switch (GET_SUM_TYPE((&sum->footer))) {
|
||||
case SUM_TYPE_NODE:
|
||||
gc_node_segment(sbi, sum->entries, segno, gc_type);
|
||||
break;
|
||||
case SUM_TYPE_DATA:
|
||||
gc_data_segment(sbi, sum->entries, ilist, segno, gc_type);
|
||||
break;
|
||||
}
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)));
|
||||
stat_inc_call_count(sbi->stat_info);
|
||||
|
||||
f2fs_put_page(sum_page, 1);
|
||||
}
|
||||
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct list_head ilist;
|
||||
unsigned int segno, i;
|
||||
int gc_type = BG_GC;
|
||||
int nfree = 0;
|
||||
int ret = -1;
|
||||
struct cp_control cpc = {
|
||||
.reason = CP_SYNC,
|
||||
};
|
||||
|
||||
INIT_LIST_HEAD(&ilist);
|
||||
gc_more:
|
||||
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
|
||||
goto stop;
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
goto stop;
|
||||
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) {
|
||||
gc_type = FG_GC;
|
||||
write_checkpoint(sbi, &cpc);
|
||||
}
|
||||
|
||||
if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE))
|
||||
goto stop;
|
||||
ret = 0;
|
||||
unsigned int segno = start_segno;
|
||||
unsigned int end_segno = start_segno + sbi->segs_per_sec;
|
||||
int seg_freed = 0;
|
||||
unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
|
||||
SUM_TYPE_DATA : SUM_TYPE_NODE;
|
||||
|
||||
/* readahead multi ssa blocks those have contiguous address */
|
||||
if (sbi->segs_per_sec > 1)
|
||||
ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec,
|
||||
META_SSA);
|
||||
ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno),
|
||||
sbi->segs_per_sec, META_SSA, true);
|
||||
|
||||
for (i = 0; i < sbi->segs_per_sec; i++)
|
||||
do_garbage_collect(sbi, segno + i, &ilist, gc_type);
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
sbi->cur_victim_sec = NULL_SEGNO;
|
||||
nfree++;
|
||||
WARN_ON(get_valid_blocks(sbi, segno, sbi->segs_per_sec));
|
||||
/* reference all summary page */
|
||||
while (segno < end_segno) {
|
||||
sum_page = get_sum_page(sbi, segno++);
|
||||
unlock_page(sum_page);
|
||||
}
|
||||
|
||||
if (has_not_enough_free_secs(sbi, nfree))
|
||||
goto gc_more;
|
||||
blk_start_plug(&plug);
|
||||
|
||||
for (segno = start_segno; segno < end_segno; segno++) {
|
||||
/* find segment summary of victim */
|
||||
sum_page = find_get_page(META_MAPPING(sbi),
|
||||
GET_SUM_BLOCK(sbi, segno));
|
||||
f2fs_bug_on(sbi, !PageUptodate(sum_page));
|
||||
f2fs_put_page(sum_page, 0);
|
||||
|
||||
sum = page_address(sum_page);
|
||||
f2fs_bug_on(sbi, type != GET_SUM_TYPE((&sum->footer)));
|
||||
|
||||
/*
|
||||
* this is to avoid deadlock:
|
||||
* - lock_page(sum_page) - f2fs_replace_block
|
||||
* - check_valid_map() - mutex_lock(sentry_lock)
|
||||
* - mutex_lock(sentry_lock) - change_curseg()
|
||||
* - lock_page(sum_page)
|
||||
*/
|
||||
|
||||
if (type == SUM_TYPE_NODE)
|
||||
gc_node_segment(sbi, sum->entries, segno, gc_type);
|
||||
else
|
||||
gc_data_segment(sbi, sum->entries, gc_list, segno,
|
||||
gc_type);
|
||||
|
||||
stat_inc_seg_count(sbi, type, gc_type);
|
||||
|
||||
f2fs_put_page(sum_page, 0);
|
||||
}
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
if (type == SUM_TYPE_NODE) {
|
||||
struct writeback_control wbc = {
|
||||
.sync_mode = WB_SYNC_ALL,
|
||||
.nr_to_write = LONG_MAX,
|
||||
.for_reclaim = 0,
|
||||
};
|
||||
sync_node_pages(sbi, 0, &wbc);
|
||||
} else {
|
||||
f2fs_submit_merged_bio(sbi, DATA, WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
while (start_segno < end_segno)
|
||||
if (get_valid_blocks(sbi, start_segno++, 1) == 0)
|
||||
seg_freed++;
|
||||
}
|
||||
|
||||
stat_inc_call_count(sbi->stat_info);
|
||||
|
||||
return seg_freed;
|
||||
}
|
||||
|
||||
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync)
|
||||
{
|
||||
unsigned int segno;
|
||||
int gc_type = sync ? FG_GC : BG_GC;
|
||||
int sec_freed = 0, seg_freed;
|
||||
int ret = -EINVAL;
|
||||
struct cp_control cpc;
|
||||
struct gc_inode_list gc_list = {
|
||||
.ilist = LIST_HEAD_INIT(gc_list.ilist),
|
||||
.iroot = RADIX_TREE_INIT(GFP_NOFS),
|
||||
};
|
||||
|
||||
cpc.reason = __get_cp_reason(sbi);
|
||||
gc_more:
|
||||
segno = NULL_SEGNO;
|
||||
|
||||
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
|
||||
goto stop;
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
ret = -EIO;
|
||||
goto stop;
|
||||
}
|
||||
|
||||
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) {
|
||||
gc_type = FG_GC;
|
||||
/*
|
||||
* If there is no victim and no prefree segment but still not
|
||||
* enough free sections, we should flush dent/node blocks and do
|
||||
* garbage collections.
|
||||
*/
|
||||
if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi))
|
||||
write_checkpoint(sbi, &cpc);
|
||||
else if (has_not_enough_free_secs(sbi, 0))
|
||||
write_checkpoint(sbi, &cpc);
|
||||
}
|
||||
|
||||
if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type))
|
||||
goto stop;
|
||||
ret = 0;
|
||||
|
||||
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type);
|
||||
|
||||
if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec)
|
||||
sec_freed++;
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
write_checkpoint(sbi, &cpc);
|
||||
sbi->cur_victim_sec = NULL_SEGNO;
|
||||
|
||||
if (!sync) {
|
||||
if (has_not_enough_free_secs(sbi, sec_freed))
|
||||
goto gc_more;
|
||||
|
||||
if (gc_type == FG_GC)
|
||||
write_checkpoint(sbi, &cpc);
|
||||
}
|
||||
stop:
|
||||
mutex_unlock(&sbi->gc_mutex);
|
||||
|
||||
put_gc_inode(&ilist);
|
||||
put_gc_inode(&gc_list);
|
||||
|
||||
if (sync)
|
||||
ret = sec_freed ? 0 : -EAGAIN;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -743,17 +929,3 @@ void build_gc_manager(struct f2fs_sb_info *sbi)
|
|||
{
|
||||
DIRTY_I(sbi)->v_ops = &default_v_ops;
|
||||
}
|
||||
|
||||
int __init create_gc_caches(void)
|
||||
{
|
||||
winode_slab = f2fs_kmem_cache_create("f2fs_gc_inodes",
|
||||
sizeof(struct inode_entry));
|
||||
if (!winode_slab)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_gc_caches(void)
|
||||
{
|
||||
kmem_cache_destroy(winode_slab);
|
||||
}
|
||||
|
|
42
fs/f2fs/gc.h
42
fs/f2fs/gc.h
|
@ -35,9 +35,9 @@ struct f2fs_gc_kthread {
|
|||
unsigned int gc_idle;
|
||||
};
|
||||
|
||||
struct inode_entry {
|
||||
struct list_head list;
|
||||
struct inode *inode;
|
||||
struct gc_inode_list {
|
||||
struct list_head ilist;
|
||||
struct radix_tree_root iroot;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -64,26 +64,26 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi)
|
|||
return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100;
|
||||
}
|
||||
|
||||
static inline long increase_sleep_time(struct f2fs_gc_kthread *gc_th, long wait)
|
||||
static inline void increase_sleep_time(struct f2fs_gc_kthread *gc_th,
|
||||
long *wait)
|
||||
{
|
||||
if (wait == gc_th->no_gc_sleep_time)
|
||||
return wait;
|
||||
if (*wait == gc_th->no_gc_sleep_time)
|
||||
return;
|
||||
|
||||
wait += gc_th->min_sleep_time;
|
||||
if (wait > gc_th->max_sleep_time)
|
||||
wait = gc_th->max_sleep_time;
|
||||
return wait;
|
||||
*wait += gc_th->min_sleep_time;
|
||||
if (*wait > gc_th->max_sleep_time)
|
||||
*wait = gc_th->max_sleep_time;
|
||||
}
|
||||
|
||||
static inline long decrease_sleep_time(struct f2fs_gc_kthread *gc_th, long wait)
|
||||
static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th,
|
||||
long *wait)
|
||||
{
|
||||
if (wait == gc_th->no_gc_sleep_time)
|
||||
wait = gc_th->max_sleep_time;
|
||||
if (*wait == gc_th->no_gc_sleep_time)
|
||||
*wait = gc_th->max_sleep_time;
|
||||
|
||||
wait -= gc_th->min_sleep_time;
|
||||
if (wait <= gc_th->min_sleep_time)
|
||||
wait = gc_th->min_sleep_time;
|
||||
return wait;
|
||||
*wait -= gc_th->min_sleep_time;
|
||||
if (*wait <= gc_th->min_sleep_time)
|
||||
*wait = gc_th->min_sleep_time;
|
||||
}
|
||||
|
||||
static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
|
||||
|
@ -100,11 +100,3 @@ static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
|
|||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int is_idle(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct block_device *bdev = sbi->sb->s_bdev;
|
||||
struct request_queue *q = bdev_get_queue(bdev);
|
||||
struct request_list *rl = &q->root_rl;
|
||||
return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]);
|
||||
}
|
||||
|
|
|
@ -79,8 +79,7 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
|
|||
const unsigned char *name = name_info->name;
|
||||
size_t len = name_info->len;
|
||||
|
||||
if ((len <= 2) && (name[0] == '.') &&
|
||||
(name[1] == '.' || name[1] == '\0'))
|
||||
if (is_dot_dotdot(name_info))
|
||||
return 0;
|
||||
|
||||
/* Initialize the default seed for the hash checksum functions */
|
||||
|
|
603
fs/f2fs/inline.c
603
fs/f2fs/inline.c
|
@ -12,38 +12,74 @@
|
|||
#include <linux/f2fs_fs.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
||||
bool f2fs_may_inline(struct inode *inode)
|
||||
bool f2fs_may_inline_data(struct inode *inode)
|
||||
{
|
||||
block_t nr_blocks;
|
||||
loff_t i_size;
|
||||
|
||||
if (!test_opt(F2FS_I_SB(inode), INLINE_DATA))
|
||||
return false;
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
return false;
|
||||
|
||||
nr_blocks = F2FS_I(inode)->i_xattr_nid ? 3 : 2;
|
||||
if (inode->i_blocks > nr_blocks)
|
||||
if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode))
|
||||
return false;
|
||||
|
||||
i_size = i_size_read(inode);
|
||||
if (i_size > MAX_INLINE_DATA)
|
||||
if (i_size_read(inode) > MAX_INLINE_DATA)
|
||||
return false;
|
||||
|
||||
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool f2fs_may_inline_dentry(struct inode *inode)
|
||||
{
|
||||
if (!test_opt(F2FS_I_SB(inode), INLINE_DENTRY))
|
||||
return false;
|
||||
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return false;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool truncate_inline_inode(struct page *ipage, u64 from)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
if (from >= MAX_INLINE_DATA)
|
||||
return false;
|
||||
|
||||
addr = inline_data_addr(ipage);
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
memset(addr + from, 0, MAX_INLINE_DATA - from);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int f2fs_read_inline_data(struct inode *inode, struct page *page)
|
||||
{
|
||||
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);
|
||||
if (IS_ERR(ipage)) {
|
||||
|
@ -51,112 +87,111 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page)
|
|||
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 */
|
||||
src_addr = inline_data_addr(ipage);
|
||||
dst_addr = kmap(page);
|
||||
memcpy(dst_addr, src_addr, MAX_INLINE_DATA);
|
||||
kunmap(page);
|
||||
f2fs_put_page(ipage, 1);
|
||||
if (page->index)
|
||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||
else
|
||||
read_inline_data(page, ipage);
|
||||
|
||||
out:
|
||||
SetPageUptodate(page);
|
||||
f2fs_put_page(ipage, 1);
|
||||
unlock_page(page);
|
||||
|
||||
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;
|
||||
block_t new_blk_addr;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(dn->inode),
|
||||
.type = DATA,
|
||||
.rw = WRITE_SYNC | REQ_PRIO,
|
||||
.page = page,
|
||||
.encrypted_page = NULL,
|
||||
};
|
||||
int dirty, err;
|
||||
|
||||
if (!f2fs_exist_data(dn->inode))
|
||||
goto clear_out;
|
||||
|
||||
err = f2fs_reserve_block(dn, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
f2fs_bug_on(F2FS_P_SB(page), PageWriteback(page));
|
||||
|
||||
read_inline_data(page, dn->inode_page);
|
||||
set_page_dirty(page);
|
||||
|
||||
/* clear dirty state */
|
||||
dirty = clear_page_dirty_for_io(page);
|
||||
|
||||
/* write data page to try to make data consistent */
|
||||
set_page_writeback(page);
|
||||
fio.old_blkaddr = dn->data_blkaddr;
|
||||
write_data_page(dn, &fio);
|
||||
f2fs_wait_on_page_writeback(page, DATA, true);
|
||||
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_inode(dn->inode_page, 0);
|
||||
clear_inline_node(dn->inode_page);
|
||||
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;
|
||||
|
||||
if (!f2fs_has_inline_data(inode))
|
||||
return 0;
|
||||
|
||||
page = grab_cache_page(inode->i_mapping, 0);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(ipage)) {
|
||||
err = PTR_ERR(ipage);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* someone else converted inline_data already */
|
||||
if (!f2fs_has_inline_data(inode))
|
||||
goto out;
|
||||
set_new_dnode(&dn, inode, ipage, ipage, 0);
|
||||
|
||||
/*
|
||||
* i_addr[0] is not used for inline data,
|
||||
* 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;
|
||||
if (f2fs_has_inline_data(inode))
|
||||
err = f2fs_convert_inline_page(&dn, page);
|
||||
|
||||
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);
|
||||
out:
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int f2fs_convert_inline_data(struct inode *inode, pgoff_t to_size,
|
||||
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)
|
||||
int f2fs_write_inline_data(struct inode *inode, struct page *page)
|
||||
{
|
||||
void *src_addr, *dst_addr;
|
||||
struct page *ipage;
|
||||
struct dnode_of_data dn;
|
||||
int err;
|
||||
|
||||
|
@ -164,49 +199,29 @@ int f2fs_write_inline_data(struct inode *inode,
|
|||
err = get_dnode_of_data(&dn, 0, LOOKUP_NODE);
|
||||
if (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)) {
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
stat_inc_inline_inode(inode);
|
||||
f2fs_put_dnode(&dn);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(inode), page->index);
|
||||
|
||||
f2fs_wait_on_page_writeback(dn.inode_page, NODE, true);
|
||||
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_DATA_EXIST);
|
||||
|
||||
sync_inode_page(&dn);
|
||||
clear_inline_node(dn.inode_page);
|
||||
f2fs_put_dnode(&dn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void truncate_inline_data(struct inode *inode, u64 from)
|
||||
{
|
||||
struct page *ipage;
|
||||
|
||||
if (from >= MAX_INLINE_DATA)
|
||||
return;
|
||||
|
||||
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return;
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
|
||||
zero_user_segment(ipage, INLINE_DATA_OFFSET + from,
|
||||
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
|
||||
set_page_dirty(ipage);
|
||||
f2fs_put_page(ipage, 1);
|
||||
}
|
||||
|
||||
bool recover_inline_data(struct inode *inode, struct page *npage)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
|
@ -231,11 +246,15 @@ process_inline:
|
|||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
f2fs_bug_on(sbi, IS_ERR(ipage));
|
||||
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
src_addr = inline_data_addr(npage);
|
||||
dst_addr = inline_data_addr(ipage);
|
||||
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);
|
||||
f2fs_put_page(ipage, 1);
|
||||
return true;
|
||||
|
@ -244,16 +263,334 @@ process_inline:
|
|||
if (f2fs_has_inline_data(inode)) {
|
||||
ipage = get_node_page(sbi, inode->i_ino);
|
||||
f2fs_bug_on(sbi, IS_ERR(ipage));
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
zero_user_segment(ipage, INLINE_DATA_OFFSET,
|
||||
INLINE_DATA_OFFSET + MAX_INLINE_DATA);
|
||||
clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
if (!truncate_inline_inode(ipage, 0))
|
||||
return false;
|
||||
f2fs_clear_inline_inode(inode);
|
||||
update_inode(inode, ipage);
|
||||
f2fs_put_page(ipage, 1);
|
||||
} else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) {
|
||||
truncate_blocks(inode, 0, false);
|
||||
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
if (truncate_blocks(inode, 0, false))
|
||||
return false;
|
||||
goto process_inline;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
|
||||
struct fscrypt_name *fname, struct page **res_page)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
|
||||
struct f2fs_inline_dentry *inline_dentry;
|
||||
struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
|
||||
struct f2fs_dir_entry *de;
|
||||
struct f2fs_dentry_ptr d;
|
||||
struct page *ipage;
|
||||
f2fs_hash_t namehash;
|
||||
|
||||
ipage = get_node_page(sbi, dir->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return NULL;
|
||||
|
||||
namehash = f2fs_dentry_hash(&name);
|
||||
|
||||
inline_dentry = inline_data_addr(ipage);
|
||||
|
||||
make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
|
||||
de = find_target_dentry(fname, namehash, 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(NULL, &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;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: ipage is grabbed by caller, but if any error occurs, we should
|
||||
* release ipage in this function.
|
||||
*/
|
||||
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) {
|
||||
f2fs_put_page(ipage, 1);
|
||||
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, true);
|
||||
zero_user_segment(page, MAX_INLINE_DATA, 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);
|
||||
memset(dentry_blk->dentry_bitmap + INLINE_DENTRY_BITMAP_SIZE, 0,
|
||||
SIZE_OF_DENTRY_BITMAP - INLINE_DENTRY_BITMAP_SIZE);
|
||||
/*
|
||||
* we do not need to zero out remainder part of dentry and filename
|
||||
* field, since we have used bitmap for marking the usage status of
|
||||
* them, besides, we can also ignore copying/zeroing reserved space
|
||||
* of dentry block, because them haven't been used so far.
|
||||
*/
|
||||
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_inode(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, nid_t ino, umode_t mode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct page *ipage;
|
||||
unsigned int bit_pos;
|
||||
f2fs_hash_t name_hash;
|
||||
size_t namelen = name->len;
|
||||
struct f2fs_inline_dentry *dentry_blk = NULL;
|
||||
struct f2fs_dentry_ptr d;
|
||||
int slots = GET_DENTRY_SLOTS(namelen);
|
||||
struct page *page = NULL;
|
||||
int err = 0;
|
||||
|
||||
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)
|
||||
return err;
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (inode) {
|
||||
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, true);
|
||||
|
||||
name_hash = f2fs_dentry_hash(name);
|
||||
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
|
||||
f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
|
||||
|
||||
set_page_dirty(ipage);
|
||||
|
||||
/* we don't need to mark_inode_dirty now */
|
||||
if (inode) {
|
||||
F2FS_I(inode)->i_pino = dir->i_ino;
|
||||
update_inode(inode, page);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
update_parent_metadata(dir, inode, 0);
|
||||
fail:
|
||||
if (inode)
|
||||
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, true);
|
||||
|
||||
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 fscrypt_str *fstr)
|
||||
{
|
||||
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(inode, &d, (void *)inline_dentry, 2);
|
||||
|
||||
if (!f2fs_fill_dentries(ctx, &d, 0, fstr))
|
||||
ctx->pos = NR_INLINE_DENTRY;
|
||||
|
||||
f2fs_put_page(ipage, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int f2fs_inline_data_fiemap(struct inode *inode,
|
||||
struct fiemap_extent_info *fieinfo, __u64 start, __u64 len)
|
||||
{
|
||||
__u64 byteaddr, ilen;
|
||||
__u32 flags = FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED |
|
||||
FIEMAP_EXTENT_LAST;
|
||||
struct node_info ni;
|
||||
struct page *ipage;
|
||||
int err = 0;
|
||||
|
||||
ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
|
||||
if (!f2fs_has_inline_data(inode)) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ilen = min_t(size_t, MAX_INLINE_DATA, i_size_read(inode));
|
||||
if (start >= ilen)
|
||||
goto out;
|
||||
if (start + len < ilen)
|
||||
ilen = start + len;
|
||||
ilen -= start;
|
||||
|
||||
get_node_info(F2FS_I_SB(inode), inode->i_ino, &ni);
|
||||
byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits;
|
||||
byteaddr += (char *)inline_data_addr(ipage) - (char *)F2FS_INODE(ipage);
|
||||
err = fiemap_fill_next_extent(fieinfo, start, byteaddr, ilen, flags);
|
||||
out:
|
||||
f2fs_put_page(ipage, 1);
|
||||
return err;
|
||||
}
|
||||
|
|
181
fs/f2fs/inode.c
181
fs/f2fs/inode.c
|
@ -12,7 +12,6 @@
|
|||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
@ -34,8 +33,8 @@ void f2fs_set_inode_flags(struct inode *inode)
|
|||
new_fl |= S_NOATIME;
|
||||
if (flags & FS_DIRSYNC_FL)
|
||||
new_fl |= S_DIRSYNC;
|
||||
set_mask_bits(&inode->i_flags,
|
||||
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
|
||||
inode_set_flags(inode, new_fl,
|
||||
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
|
||||
}
|
||||
|
||||
static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
||||
|
@ -51,6 +50,15 @@ static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
|||
}
|
||||
}
|
||||
|
||||
static bool __written_first_block(struct f2fs_inode *ri)
|
||||
{
|
||||
block_t addr = le32_to_cpu(ri->i_addr[0]);
|
||||
|
||||
if (addr != NEW_ADDR && addr != NULL_ADDR)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
||||
{
|
||||
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
|
||||
|
@ -67,6 +75,25 @@ static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
|
|||
}
|
||||
}
|
||||
|
||||
static void __recover_inline_status(struct inode *inode, struct page *ipage)
|
||||
{
|
||||
void *inline_data = inline_data_addr(ipage);
|
||||
__le32 *start = inline_data;
|
||||
__le32 *end = start + MAX_INLINE_DATA / sizeof(__le32);
|
||||
|
||||
while (start < end) {
|
||||
if (*start++) {
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
|
||||
set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
|
||||
set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage));
|
||||
set_page_dirty(ipage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int do_read_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
|
@ -111,13 +138,27 @@ static int do_read_inode(struct inode *inode)
|
|||
fi->i_pino = le32_to_cpu(ri->i_pino);
|
||||
fi->i_dir_level = ri->i_dir_level;
|
||||
|
||||
get_extent_info(&fi->ext, ri->i_ext);
|
||||
if (f2fs_init_extent_tree(inode, &ri->i_ext))
|
||||
set_page_dirty(node_page);
|
||||
|
||||
get_inline_info(fi, ri);
|
||||
|
||||
/* check data exist */
|
||||
if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode))
|
||||
__recover_inline_status(inode, node_page);
|
||||
|
||||
/* get rdev by using inline_info */
|
||||
__get_inode_rdev(inode, ri);
|
||||
|
||||
if (__written_first_block(ri))
|
||||
set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
|
||||
|
||||
f2fs_put_page(node_page, 1);
|
||||
|
||||
stat_inc_inline_xattr(inode);
|
||||
stat_inc_inline_inode(inode);
|
||||
stat_inc_inline_dir(inode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -156,9 +197,12 @@ make_now:
|
|||
inode->i_op = &f2fs_dir_inode_operations;
|
||||
inode->i_fop = &f2fs_dir_operations;
|
||||
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)) {
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
if (f2fs_encrypted_inode(inode))
|
||||
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
|
||||
else
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
|
||||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||
|
@ -178,11 +222,11 @@ bad_inode:
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void update_inode(struct inode *inode, struct page *node_page)
|
||||
int update_inode(struct inode *inode, struct page *node_page)
|
||||
{
|
||||
struct f2fs_inode *ri;
|
||||
|
||||
f2fs_wait_on_page_writeback(node_page, NODE);
|
||||
f2fs_wait_on_page_writeback(node_page, NODE, true);
|
||||
|
||||
ri = F2FS_INODE(node_page);
|
||||
|
||||
|
@ -193,7 +237,12 @@ void update_inode(struct inode *inode, struct page *node_page)
|
|||
ri->i_links = cpu_to_le32(inode->i_nlink);
|
||||
ri->i_size = cpu_to_le64(i_size_read(inode));
|
||||
ri->i_blocks = cpu_to_le64(inode->i_blocks);
|
||||
set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext);
|
||||
|
||||
if (F2FS_I(inode)->extent_tree)
|
||||
set_raw_extent(&F2FS_I(inode)->extent_tree->largest,
|
||||
&ri->i_ext);
|
||||
else
|
||||
memset(&ri->i_ext, 0, sizeof(ri->i_ext));
|
||||
set_raw_inline(F2FS_I(inode), ri);
|
||||
|
||||
ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec);
|
||||
|
@ -211,15 +260,20 @@ void update_inode(struct inode *inode, struct page *node_page)
|
|||
|
||||
__set_inode_rdev(inode, ri);
|
||||
set_cold_node(inode, node_page);
|
||||
set_page_dirty(node_page);
|
||||
|
||||
clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
|
||||
|
||||
/* deleted inode */
|
||||
if (inode->i_nlink == 0)
|
||||
clear_inline_node(node_page);
|
||||
|
||||
return set_page_dirty(node_page);
|
||||
}
|
||||
|
||||
void update_inode_page(struct inode *inode)
|
||||
int update_inode_page(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct page *node_page;
|
||||
int ret = 0;
|
||||
retry:
|
||||
node_page = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(node_page)) {
|
||||
|
@ -230,10 +284,11 @@ retry:
|
|||
} else if (err != -ENOENT) {
|
||||
f2fs_stop_checkpoint(sbi);
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
update_inode(inode, node_page);
|
||||
ret = update_inode(inode, node_page);
|
||||
f2fs_put_page(node_page, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
|
@ -248,16 +303,11 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||
return 0;
|
||||
|
||||
/*
|
||||
* We need to lock here to prevent from producing dirty node pages
|
||||
* We need to balance fs here to prevent from producing dirty node pages
|
||||
* during the urgent cleaning time when runing out of free sections.
|
||||
*/
|
||||
f2fs_lock_op(sbi);
|
||||
update_inode_page(inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
if (wbc)
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
if (update_inode_page(inode))
|
||||
f2fs_balance_fs(sbi, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -267,47 +317,78 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||
void f2fs_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
nid_t xnid = fi->i_xattr_nid;
|
||||
int err = 0;
|
||||
|
||||
/* some remained atomic pages should discarded */
|
||||
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode))
|
||||
commit_inmem_pages(inode, true);
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
drop_inmem_pages(inode);
|
||||
|
||||
trace_f2fs_evict_inode(inode);
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
truncate_inode_pages(&inode->i_data, 0);
|
||||
|
||||
if (inode->i_ino == F2FS_NODE_INO(sbi) ||
|
||||
inode->i_ino == F2FS_META_INO(sbi))
|
||||
goto out_clear;
|
||||
|
||||
f2fs_bug_on(sbi, get_dirty_pages(inode));
|
||||
remove_dirty_dir_inode(inode);
|
||||
remove_dirty_inode(inode);
|
||||
|
||||
f2fs_destroy_extent_tree(inode);
|
||||
|
||||
if (inode->i_nlink || is_bad_inode(inode))
|
||||
goto no_delete;
|
||||
|
||||
sb_start_intwrite(inode->i_sb);
|
||||
set_inode_flag(F2FS_I(inode), FI_NO_ALLOC);
|
||||
set_inode_flag(fi, FI_NO_ALLOC);
|
||||
i_size_write(inode, 0);
|
||||
|
||||
if (F2FS_HAS_BLOCKS(inode))
|
||||
f2fs_truncate(inode);
|
||||
err = f2fs_truncate(inode, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
remove_inode_page(inode);
|
||||
stat_dec_inline_inode(inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
if (!err) {
|
||||
f2fs_lock_op(sbi);
|
||||
err = remove_inode_page(inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
}
|
||||
|
||||
sb_end_intwrite(inode->i_sb);
|
||||
no_delete:
|
||||
stat_dec_inline_xattr(inode);
|
||||
stat_dec_inline_dir(inode);
|
||||
stat_dec_inline_inode(inode);
|
||||
|
||||
invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino);
|
||||
if (xnid)
|
||||
invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid);
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_APPEND_WRITE))
|
||||
add_dirty_inode(sbi, inode->i_ino, APPEND_INO);
|
||||
if (is_inode_flag_set(F2FS_I(inode), FI_UPDATE_WRITE))
|
||||
add_dirty_inode(sbi, inode->i_ino, UPDATE_INO);
|
||||
if (is_inode_flag_set(fi, FI_APPEND_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, APPEND_INO);
|
||||
if (is_inode_flag_set(fi, FI_UPDATE_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
|
||||
if (is_inode_flag_set(fi, FI_FREE_NID)) {
|
||||
if (err && err != -ENOENT)
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
else
|
||||
alloc_nid_failed(sbi, inode->i_ino);
|
||||
clear_inode_flag(fi, FI_FREE_NID);
|
||||
}
|
||||
|
||||
if (err && err != -ENOENT) {
|
||||
if (!exist_written_data(sbi, inode->i_ino, ORPHAN_INO)) {
|
||||
/*
|
||||
* get here because we failed to release resource
|
||||
* of inode previously, reminder our user to run fsck
|
||||
* for fixing.
|
||||
*/
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"inode (ino:%lu) resource leak, run fsck "
|
||||
"to fix this issue!", inode->i_ino);
|
||||
}
|
||||
}
|
||||
out_clear:
|
||||
fscrypt_put_encryption_info(inode, NULL);
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
|
@ -315,6 +396,7 @@ out_clear:
|
|||
void handle_failed_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
int err = 0;
|
||||
|
||||
clear_nlink(inode);
|
||||
make_bad_inode(inode);
|
||||
|
@ -322,12 +404,29 @@ void handle_failed_inode(struct inode *inode)
|
|||
|
||||
i_size_write(inode, 0);
|
||||
if (F2FS_HAS_BLOCKS(inode))
|
||||
f2fs_truncate(inode);
|
||||
err = f2fs_truncate(inode, false);
|
||||
|
||||
remove_inode_page(inode);
|
||||
stat_dec_inline_inode(inode);
|
||||
if (!err)
|
||||
err = remove_inode_page(inode);
|
||||
|
||||
alloc_nid_failed(sbi, inode->i_ino);
|
||||
/*
|
||||
* if we skip truncate_node in remove_inode_page bacause we failed
|
||||
* before, it's better to find another way to release resource of
|
||||
* this inode (e.g. valid block count, node block or nid). Here we
|
||||
* choose to add this inode to orphan list, so that we can call iput
|
||||
* for releasing in orphan recovery flow.
|
||||
*
|
||||
* Note: we should add inode to orphan list before f2fs_unlock_op()
|
||||
* so we can prevent losing this orphan when encoutering checkpoint
|
||||
* and following suddenly power-off.
|
||||
*/
|
||||
if (err && err != -ENOENT) {
|
||||
err = acquire_orphan_inode(sbi);
|
||||
if (!err)
|
||||
add_orphan_inode(sbi, inode->i_ino);
|
||||
}
|
||||
|
||||
set_inode_flag(F2FS_I(inode), FI_FREE_NID);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
/* iput will drop the inode object */
|
||||
|
|
591
fs/f2fs/namei.c
591
fs/f2fs/namei.c
|
@ -9,11 +9,13 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
|
@ -52,21 +54,34 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
|
|||
if (err) {
|
||||
err = -EINVAL;
|
||||
nid_free = true;
|
||||
goto out;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* If the directory encrypted, then we should encrypt the inode. */
|
||||
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode))
|
||||
f2fs_set_encrypted_inode(inode);
|
||||
|
||||
if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode))
|
||||
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
|
||||
if (f2fs_may_inline_dentry(inode))
|
||||
set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY);
|
||||
|
||||
f2fs_init_extent_tree(inode, NULL);
|
||||
|
||||
stat_inc_inline_xattr(inode);
|
||||
stat_inc_inline_inode(inode);
|
||||
stat_inc_inline_dir(inode);
|
||||
|
||||
trace_f2fs_new_inode(inode, 0);
|
||||
mark_inode_dirty(inode);
|
||||
return inode;
|
||||
|
||||
out:
|
||||
clear_nlink(inode);
|
||||
unlock_new_inode(inode);
|
||||
fail:
|
||||
trace_f2fs_new_inode(inode, err);
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
if (nid_free)
|
||||
alloc_nid_failed(sbi, ino);
|
||||
set_inode_flag(F2FS_I(inode), FI_FREE_NID);
|
||||
iput(inode);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
@ -75,7 +90,14 @@ static int is_multimedia_file(const unsigned char *s, const char *sub)
|
|||
size_t slen = strlen(s);
|
||||
size_t sublen = strlen(sub);
|
||||
|
||||
if (sublen > slen)
|
||||
/*
|
||||
* filename format of multimedia file should be defined as:
|
||||
* "filename + '.' + extension".
|
||||
*/
|
||||
if (slen < sublen + 2)
|
||||
return 0;
|
||||
|
||||
if (s[slen - sublen - 1] != '.')
|
||||
return 0;
|
||||
|
||||
return !strncasecmp(s + slen - sublen, sub, sublen);
|
||||
|
@ -107,8 +129,6 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
nid_t ino = 0;
|
||||
int err;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
@ -121,6 +141,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
ino = inode->i_ino;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = f2fs_add_link(dentry, inode);
|
||||
if (err)
|
||||
|
@ -131,6 +153,9 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
|
||||
d_instantiate(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
return 0;
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
|
@ -144,7 +169,11 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
|
|||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
int err;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
if (f2fs_encrypted_inode(dir) &&
|
||||
!fscrypt_has_permitted_context(dir, inode))
|
||||
return -EPERM;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
inode->i_ctime = CURRENT_TIME;
|
||||
ihold(inode);
|
||||
|
@ -157,6 +186,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
|
|||
f2fs_unlock_op(sbi);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
return 0;
|
||||
out:
|
||||
clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
|
||||
|
@ -174,30 +206,110 @@ struct dentry *f2fs_get_parent(struct dentry *child)
|
|||
return d_obtain_alias(f2fs_iget(child->d_inode->i_sb, ino));
|
||||
}
|
||||
|
||||
static int __recover_dot_dentries(struct inode *dir, nid_t pino)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct qstr dot = {.len = 1, .name = "."};
|
||||
struct qstr dotdot = {.len = 2, .name = ".."};
|
||||
struct f2fs_dir_entry *de;
|
||||
struct page *page;
|
||||
int err = 0;
|
||||
|
||||
if (f2fs_readonly(sbi->sb)) {
|
||||
f2fs_msg(sbi->sb, KERN_INFO,
|
||||
"skip recovering inline_dots inode (ino:%lu, pino:%u) "
|
||||
"in readonly mountpoint", dir->i_ino, pino);
|
||||
return 0;
|
||||
}
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
de = f2fs_find_entry(dir, &dot, &page);
|
||||
if (de) {
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
} else {
|
||||
err = __f2fs_add_link(dir, &dot, NULL, dir->i_ino, S_IFDIR);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
de = f2fs_find_entry(dir, &dotdot, &page);
|
||||
if (de) {
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
} else {
|
||||
err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR);
|
||||
}
|
||||
out:
|
||||
if (!err) {
|
||||
clear_inode_flag(F2FS_I(dir), FI_INLINE_DOTS);
|
||||
mark_inode_dirty(dir);
|
||||
}
|
||||
|
||||
f2fs_unlock_op(sbi);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct inode *inode = NULL;
|
||||
struct f2fs_dir_entry *de;
|
||||
struct page *page;
|
||||
nid_t ino;
|
||||
int err = 0;
|
||||
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
int res = fscrypt_get_encryption_info(dir);
|
||||
|
||||
/*
|
||||
* DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is
|
||||
* created while the directory was encrypted and we
|
||||
* don't have access to the key.
|
||||
*/
|
||||
if (fscrypt_has_encryption_key(dir))
|
||||
fscrypt_set_encrypted_dentry(dentry);
|
||||
fscrypt_set_d_op(dentry);
|
||||
if (res && res != -ENOKEY)
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
if (dentry->d_name.len > F2FS_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
de = f2fs_find_entry(dir, &dentry->d_name, &page);
|
||||
if (de) {
|
||||
nid_t ino = le32_to_cpu(de->ino);
|
||||
kunmap(page);
|
||||
f2fs_put_page(page, 0);
|
||||
if (!de)
|
||||
return d_splice_alias(inode, dentry);
|
||||
|
||||
inode = f2fs_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
ino = le32_to_cpu(de->ino);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
|
||||
stat_inc_inline_inode(inode);
|
||||
inode = f2fs_iget(dir->i_sb, ino);
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
|
||||
if (f2fs_has_inline_dots(inode)) {
|
||||
err = __recover_dot_dentries(inode, dir->i_ino);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
if (!IS_ERR(inode) && f2fs_encrypted_inode(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
bool nokey = f2fs_encrypted_inode(inode) &&
|
||||
!fscrypt_has_encryption_key(inode);
|
||||
err = nokey ? -ENOKEY : -EPERM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return d_splice_alias(inode, dentry);
|
||||
|
||||
err_out:
|
||||
iput(inode);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
|
@ -209,58 +321,155 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
|
|||
int err = -ENOENT;
|
||||
|
||||
trace_f2fs_unlink_enter(dir, dentry);
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
de = f2fs_find_entry(dir, &dentry->d_name, &page);
|
||||
if (!de)
|
||||
goto fail;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = acquire_orphan_inode(sbi);
|
||||
if (err) {
|
||||
f2fs_unlock_op(sbi);
|
||||
kunmap(page);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
goto fail;
|
||||
}
|
||||
f2fs_delete_entry(de, page, inode);
|
||||
f2fs_delete_entry(de, page, dir, inode);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
/* In order to evict this inode, we set it dirty */
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
fail:
|
||||
trace_f2fs_unlink_exit(inode, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct page *page;
|
||||
char *link;
|
||||
|
||||
page = page_follow_link_light(dentry, nd);
|
||||
if (IS_ERR(page))
|
||||
return page;
|
||||
|
||||
link = nd_get_link(nd);
|
||||
if (IS_ERR(link))
|
||||
return link;
|
||||
|
||||
/* this is broken symlink case */
|
||||
if (*link == 0) {
|
||||
kunmap(page);
|
||||
page_cache_release(page);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *symname)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct inode *inode;
|
||||
size_t symlen = strlen(symname) + 1;
|
||||
size_t len = strlen(symname);
|
||||
struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
|
||||
struct fscrypt_symlink_data *sd = NULL;
|
||||
int err;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
err = fscrypt_get_encryption_info(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!fscrypt_has_encryption_key(dir))
|
||||
return -EPERM;
|
||||
|
||||
disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
|
||||
sizeof(struct fscrypt_symlink_data));
|
||||
}
|
||||
|
||||
if (disk_link.len > dir->i_sb->s_blocksize)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
if (f2fs_encrypted_inode(inode))
|
||||
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
|
||||
else
|
||||
inode->i_op = &f2fs_symlink_inode_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = f2fs_add_link(dentry, inode);
|
||||
if (err)
|
||||
goto out;
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
err = page_symlink(inode, symname, symlen);
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
|
||||
if (f2fs_encrypted_inode(inode)) {
|
||||
struct qstr istr = QSTR_INIT(symname, len);
|
||||
struct fscrypt_str ostr;
|
||||
|
||||
sd = kzalloc(disk_link.len, GFP_NOFS);
|
||||
if (!sd) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = fscrypt_get_encryption_info(inode);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
if (!fscrypt_has_encryption_key(inode)) {
|
||||
err = -EPERM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ostr.name = sd->encrypted_path;
|
||||
ostr.len = disk_link.len;
|
||||
err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
sd->len = cpu_to_le16(ostr.len);
|
||||
disk_link.name = (char *)sd;
|
||||
}
|
||||
|
||||
err = page_symlink(inode, disk_link.name, disk_link.len);
|
||||
|
||||
err_out:
|
||||
d_instantiate(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
/*
|
||||
* Let's flush symlink data in order to avoid broken symlink as much as
|
||||
* possible. Nevertheless, fsyncing is the best way, but there is no
|
||||
* way to get a file descriptor in order to flush that.
|
||||
*
|
||||
* Note that, it needs to do dir->fsync to make this recoverable.
|
||||
* If the symlink path is stored into inline_data, there is no
|
||||
* performance regression.
|
||||
*/
|
||||
if (!err) {
|
||||
filemap_write_and_wait_range(inode->i_mapping, 0,
|
||||
disk_link.len - 1);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
} else {
|
||||
f2fs_unlink(dir, dentry);
|
||||
}
|
||||
|
||||
kfree(sd);
|
||||
return err;
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
|
@ -273,8 +482,6 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
struct inode *inode;
|
||||
int err;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
inode = f2fs_new_inode(dir, S_IFDIR | mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
@ -282,7 +489,9 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
inode->i_op = &f2fs_dir_inode_operations;
|
||||
inode->i_fop = &f2fs_dir_operations;
|
||||
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);
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
set_inode_flag(F2FS_I(inode), FI_INC_LINK);
|
||||
f2fs_lock_op(sbi);
|
||||
|
@ -296,6 +505,8 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
d_instantiate(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
return 0;
|
||||
|
||||
out_fail:
|
||||
|
@ -322,8 +533,6 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
|
|||
if (!new_valid_dev(rdev))
|
||||
return -EINVAL;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
@ -331,6 +540,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
|
|||
init_special_inode(inode, inode->i_mode, rdev);
|
||||
inode->i_op = &f2fs_special_inode_operations;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = f2fs_add_link(dentry, inode);
|
||||
if (err)
|
||||
|
@ -338,28 +549,111 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
|
|||
f2fs_unlock_op(sbi);
|
||||
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
|
||||
if (IS_DIRSYNC(dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
return 0;
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, struct inode **whiteout)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct inode *inode;
|
||||
int err;
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
||||
if (whiteout) {
|
||||
init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
|
||||
inode->i_op = &f2fs_special_inode_operations;
|
||||
} else {
|
||||
inode->i_op = &f2fs_file_inode_operations;
|
||||
inode->i_fop = &f2fs_file_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
}
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = acquire_orphan_inode(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = f2fs_do_tmpfile(inode, dir);
|
||||
if (err)
|
||||
goto release_out;
|
||||
|
||||
/*
|
||||
* add this non-linked tmpfile to orphan list, in this way we could
|
||||
* remove all unused data of tmpfile after abnormal power-off.
|
||||
*/
|
||||
add_orphan_inode(sbi, inode->i_ino);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
|
||||
if (whiteout) {
|
||||
inode_dec_link_count(inode);
|
||||
*whiteout = inode;
|
||||
} else {
|
||||
d_tmpfile(dentry, inode);
|
||||
}
|
||||
unlock_new_inode(inode);
|
||||
return 0;
|
||||
|
||||
release_out:
|
||||
release_orphan_inode(sbi);
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
if (f2fs_encrypted_inode(dir)) {
|
||||
int err = fscrypt_get_encryption_info(dir);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return __f2fs_tmpfile(dir, dentry, mode, NULL);
|
||||
}
|
||||
|
||||
static int f2fs_create_whiteout(struct inode *dir, struct inode **whiteout)
|
||||
{
|
||||
return __f2fs_tmpfile(dir, NULL, S_IFCHR | WHITEOUT_MODE, whiteout);
|
||||
}
|
||||
|
||||
static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(old_dir);
|
||||
struct inode *old_inode = old_dentry->d_inode;
|
||||
struct inode *new_inode = new_dentry->d_inode;
|
||||
struct inode *whiteout = NULL;
|
||||
struct page *old_dir_page;
|
||||
struct page *old_page, *new_page;
|
||||
struct page *old_page, *new_page = NULL;
|
||||
struct f2fs_dir_entry *old_dir_entry = NULL;
|
||||
struct f2fs_dir_entry *old_entry;
|
||||
struct f2fs_dir_entry *new_entry;
|
||||
bool is_old_inline = f2fs_has_inline_dentry(old_dir);
|
||||
int err = -ENOENT;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
|
||||
!fscrypt_has_permitted_context(new_dir, old_inode)) {
|
||||
err = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
||||
if (!old_entry)
|
||||
|
@ -372,17 +666,25 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
goto out_old;
|
||||
}
|
||||
|
||||
if (flags & RENAME_WHITEOUT) {
|
||||
err = f2fs_create_whiteout(old_dir, &whiteout);
|
||||
if (err)
|
||||
goto out_dir;
|
||||
}
|
||||
|
||||
if (new_inode) {
|
||||
|
||||
err = -ENOTEMPTY;
|
||||
if (old_dir_entry && !f2fs_empty_dir(new_inode))
|
||||
goto out_dir;
|
||||
goto out_whiteout;
|
||||
|
||||
err = -ENOENT;
|
||||
new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name,
|
||||
&new_page);
|
||||
if (!new_entry)
|
||||
goto out_dir;
|
||||
goto out_whiteout;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
|
@ -390,7 +692,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
if (err)
|
||||
goto put_out_dir;
|
||||
|
||||
if (update_dent_inode(old_inode, &new_dentry->d_name)) {
|
||||
err = update_dent_inode(old_inode, new_inode,
|
||||
&new_dentry->d_name);
|
||||
if (err) {
|
||||
release_orphan_inode(sbi);
|
||||
goto put_out_dir;
|
||||
}
|
||||
|
@ -414,36 +718,70 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
update_inode_page(old_inode);
|
||||
update_inode_page(new_inode);
|
||||
} else {
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
err = f2fs_add_link(new_dentry, old_inode);
|
||||
if (err) {
|
||||
f2fs_unlock_op(sbi);
|
||||
goto out_dir;
|
||||
goto out_whiteout;
|
||||
}
|
||||
|
||||
if (old_dir_entry) {
|
||||
inc_nlink(new_dir);
|
||||
update_inode_page(new_dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* old entry and new entry can locate in the same inline
|
||||
* dentry in inode, when attaching new entry in inline dentry,
|
||||
* it could force inline dentry conversion, after that,
|
||||
* old_entry and old_page will point to wrong address, in
|
||||
* order to avoid this, let's do the check and update here.
|
||||
*/
|
||||
if (is_old_inline && !f2fs_has_inline_dentry(old_dir)) {
|
||||
f2fs_put_page(old_page, 0);
|
||||
old_page = NULL;
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir,
|
||||
&old_dentry->d_name, &old_page);
|
||||
if (!old_entry) {
|
||||
err = -EIO;
|
||||
f2fs_unlock_op(sbi);
|
||||
goto out_dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
down_write(&F2FS_I(old_inode)->i_sem);
|
||||
file_lost_pino(old_inode);
|
||||
if (new_inode && file_enc_name(new_inode))
|
||||
file_set_enc_name(old_inode);
|
||||
up_write(&F2FS_I(old_inode)->i_sem);
|
||||
|
||||
old_inode->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty(old_inode);
|
||||
|
||||
f2fs_delete_entry(old_entry, old_page, NULL);
|
||||
f2fs_delete_entry(old_entry, old_page, old_dir, NULL);
|
||||
|
||||
if (whiteout) {
|
||||
whiteout->i_state |= I_LINKABLE;
|
||||
set_inode_flag(F2FS_I(whiteout), FI_INC_LINK);
|
||||
err = f2fs_add_link(old_dentry, whiteout);
|
||||
if (err)
|
||||
goto put_out_dir;
|
||||
whiteout->i_state &= ~I_LINKABLE;
|
||||
iput(whiteout);
|
||||
}
|
||||
|
||||
if (old_dir_entry) {
|
||||
if (old_dir != new_dir) {
|
||||
if (old_dir != new_dir && !whiteout) {
|
||||
f2fs_set_link(old_inode, old_dir_entry,
|
||||
old_dir_page, new_dir);
|
||||
update_inode_page(old_inode);
|
||||
} else {
|
||||
kunmap(old_dir_page);
|
||||
f2fs_dentry_kunmap(old_inode, old_dir_page);
|
||||
f2fs_put_page(old_dir_page, 0);
|
||||
}
|
||||
drop_nlink(old_dir);
|
||||
|
@ -452,19 +790,27 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
}
|
||||
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
return 0;
|
||||
|
||||
put_out_dir:
|
||||
f2fs_unlock_op(sbi);
|
||||
kunmap(new_page);
|
||||
f2fs_put_page(new_page, 0);
|
||||
if (new_page) {
|
||||
f2fs_dentry_kunmap(new_dir, new_page);
|
||||
f2fs_put_page(new_page, 0);
|
||||
}
|
||||
out_whiteout:
|
||||
if (whiteout)
|
||||
iput(whiteout);
|
||||
out_dir:
|
||||
if (old_dir_entry) {
|
||||
kunmap(old_dir_page);
|
||||
f2fs_dentry_kunmap(old_inode, old_dir_page);
|
||||
f2fs_put_page(old_dir_page, 0);
|
||||
}
|
||||
out_old:
|
||||
kunmap(old_page);
|
||||
f2fs_dentry_kunmap(old_dir, old_page);
|
||||
f2fs_put_page(old_page, 0);
|
||||
out:
|
||||
return err;
|
||||
|
@ -483,7 +829,11 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
int old_nlink = 0, new_nlink = 0;
|
||||
int err = -ENOENT;
|
||||
|
||||
f2fs_balance_fs(sbi);
|
||||
if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
|
||||
(old_dir != new_dir) &&
|
||||
(!fscrypt_has_permitted_context(new_dir, old_inode) ||
|
||||
!fscrypt_has_permitted_context(old_dir, new_inode)))
|
||||
return -EPERM;
|
||||
|
||||
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
||||
if (!old_entry)
|
||||
|
@ -527,15 +877,21 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
goto out_new_dir;
|
||||
}
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
err = update_dent_inode(old_inode, &new_dentry->d_name);
|
||||
err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
if (file_enc_name(new_inode))
|
||||
file_set_enc_name(old_inode);
|
||||
|
||||
err = update_dent_inode(new_inode, &old_dentry->d_name);
|
||||
err = update_dent_inode(new_inode, old_inode, &old_dentry->d_name);
|
||||
if (err)
|
||||
goto out_undo;
|
||||
if (file_enc_name(old_inode))
|
||||
file_set_enc_name(new_inode);
|
||||
|
||||
/* update ".." directory entry info of old dentry */
|
||||
if (old_dir_entry)
|
||||
|
@ -588,27 +944,33 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
update_inode_page(new_dir);
|
||||
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
|
||||
f2fs_sync_fs(sbi->sb, 1);
|
||||
return 0;
|
||||
out_undo:
|
||||
/* Still we may fail to recover name info of f2fs_inode here */
|
||||
update_dent_inode(old_inode, &old_dentry->d_name);
|
||||
/*
|
||||
* Still we may fail to recover name info of f2fs_inode here
|
||||
* Drop it, once its name is set as encrypted
|
||||
*/
|
||||
update_dent_inode(old_inode, old_inode, &old_dentry->d_name);
|
||||
out_unlock:
|
||||
f2fs_unlock_op(sbi);
|
||||
out_new_dir:
|
||||
if (new_dir_entry) {
|
||||
kunmap(new_dir_page);
|
||||
f2fs_dentry_kunmap(new_inode, new_dir_page);
|
||||
f2fs_put_page(new_dir_page, 0);
|
||||
}
|
||||
out_old_dir:
|
||||
if (old_dir_entry) {
|
||||
kunmap(old_dir_page);
|
||||
f2fs_dentry_kunmap(old_inode, old_dir_page);
|
||||
f2fs_put_page(old_dir_page, 0);
|
||||
}
|
||||
out_new:
|
||||
kunmap(new_page);
|
||||
f2fs_dentry_kunmap(new_dir, new_page);
|
||||
f2fs_put_page(new_page, 0);
|
||||
out_old:
|
||||
kunmap(old_page);
|
||||
f2fs_dentry_kunmap(old_dir, old_page);
|
||||
f2fs_put_page(old_page, 0);
|
||||
out:
|
||||
return err;
|
||||
|
@ -618,7 +980,7 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry,
|
|||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
|
||||
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & RENAME_EXCHANGE) {
|
||||
|
@ -629,51 +991,100 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry,
|
|||
* VFS has already handled the new dentry existence case,
|
||||
* here, we just deal with "RENAME_NOREPLACE" as regular rename.
|
||||
*/
|
||||
return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry);
|
||||
return f2fs_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
|
||||
}
|
||||
|
||||
static int f2fs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
static void *f2fs_encrypted_follow_link(struct dentry *dentry,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct inode *inode;
|
||||
int err;
|
||||
struct page *cpage = NULL;
|
||||
char *caddr, *paddr = NULL;
|
||||
struct fscrypt_str cstr = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_str pstr = FSTR_INIT(NULL, 0);
|
||||
struct fscrypt_symlink_data *sd;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
|
||||
u32 max_size = inode->i_sb->s_blocksize;
|
||||
int res;
|
||||
|
||||
inode = f2fs_new_inode(dir, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
res = fscrypt_get_encryption_info(inode);
|
||||
if (res)
|
||||
return ERR_PTR(res);
|
||||
|
||||
inode->i_op = &f2fs_file_inode_operations;
|
||||
inode->i_fop = &f2fs_file_operations;
|
||||
inode->i_mapping->a_ops = &f2fs_dblock_aops;
|
||||
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
|
||||
if (IS_ERR(cpage))
|
||||
return cpage;
|
||||
caddr = kmap(cpage);
|
||||
caddr[size] = 0;
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
err = acquire_orphan_inode(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
/* Symlink is encrypted */
|
||||
sd = (struct fscrypt_symlink_data *)caddr;
|
||||
cstr.name = sd->encrypted_path;
|
||||
cstr.len = le16_to_cpu(sd->len);
|
||||
|
||||
err = f2fs_do_tmpfile(inode, dir);
|
||||
if (err)
|
||||
goto release_out;
|
||||
/* this is broken symlink case */
|
||||
if (unlikely(cstr.len == 0)) {
|
||||
res = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/*
|
||||
* add this non-linked tmpfile to orphan list, in this way we could
|
||||
* remove all unused data of tmpfile after abnormal power-off.
|
||||
*/
|
||||
add_orphan_inode(sbi, inode->i_ino);
|
||||
f2fs_unlock_op(sbi);
|
||||
/* this is broken symlink case */
|
||||
if (unlikely(cstr.name[0] == 0)) {
|
||||
res = -ENOENT;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
d_tmpfile(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
return 0;
|
||||
if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > max_size) {
|
||||
/* Symlink data on the disk is corrupted */
|
||||
res = -EIO;
|
||||
goto errout;
|
||||
}
|
||||
res = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
|
||||
if (res)
|
||||
goto errout;
|
||||
|
||||
release_out:
|
||||
release_orphan_inode(sbi);
|
||||
out:
|
||||
handle_failed_inode(inode);
|
||||
return err;
|
||||
res = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
|
||||
if (res < 0)
|
||||
goto errout;
|
||||
|
||||
paddr = pstr.name;
|
||||
|
||||
/* Null-terminate the name */
|
||||
paddr[res] = '\0';
|
||||
nd_set_link(nd, paddr);
|
||||
|
||||
kunmap(cpage);
|
||||
page_cache_release(cpage);
|
||||
return NULL;
|
||||
errout:
|
||||
fscrypt_fname_free_buffer(&pstr);
|
||||
kunmap(cpage);
|
||||
page_cache_release(cpage);
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
void kfree_put_link(struct dentry *dentry, struct nameidata *nd,
|
||||
void *cookie)
|
||||
{
|
||||
char *s = nd_get_link(nd);
|
||||
if (!IS_ERR(s))
|
||||
kfree(s);
|
||||
}
|
||||
|
||||
const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = f2fs_encrypted_follow_link,
|
||||
.put_link = kfree_put_link,
|
||||
.getattr = f2fs_getattr,
|
||||
.setattr = f2fs_setattr,
|
||||
#ifdef CONFIG_F2FS_FS_XATTR
|
||||
.setxattr = generic_setxattr,
|
||||
.getxattr = generic_getxattr,
|
||||
.listxattr = f2fs_listxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct inode_operations f2fs_dir_inode_operations = {
|
||||
.create = f2fs_create,
|
||||
.lookup = f2fs_lookup,
|
||||
|
@ -699,7 +1110,7 @@ const struct inode_operations f2fs_dir_inode_operations = {
|
|||
|
||||
const struct inode_operations f2fs_symlink_inode_operations = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = page_follow_link_light,
|
||||
.follow_link = f2fs_follow_link,
|
||||
.put_link = page_put_link,
|
||||
.getattr = f2fs_getattr,
|
||||
.setattr = f2fs_setattr,
|
||||
|
|
776
fs/f2fs/node.c
776
fs/f2fs/node.c
File diff suppressed because it is too large
Load diff
112
fs/f2fs/node.h
112
fs/f2fs/node.h
|
@ -14,21 +14,35 @@
|
|||
/* node block offset on the NAT area dedicated to the given start node id */
|
||||
#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK)
|
||||
|
||||
/* # of pages to perform readahead before building free nids */
|
||||
/* # of pages to perform synchronous readahead before building free nids */
|
||||
#define FREE_NID_PAGES 4
|
||||
|
||||
#define DEF_RA_NID_PAGES 4 /* # of nid pages to be readaheaded */
|
||||
|
||||
/* maximum readahead size for node during getting data blocks */
|
||||
#define MAX_RA_NODE 128
|
||||
|
||||
/* control the memory footprint threshold (10MB per 1GB ram) */
|
||||
#define DEF_RAM_THRESHOLD 10
|
||||
|
||||
/* control dirty nats ratio threshold (default: 10% over max nid count) */
|
||||
#define DEF_DIRTY_NAT_RATIO_THRESHOLD 10
|
||||
|
||||
/* vector size for gang look-up from nat cache that consists of radix tree */
|
||||
#define NATVEC_SIZE 64
|
||||
#define SETVEC_SIZE 32
|
||||
|
||||
/* return value for read_node_page */
|
||||
#define LOCKED_PAGE 1
|
||||
|
||||
/* For flag in struct node_info */
|
||||
enum {
|
||||
IS_CHECKPOINTED, /* is it checkpointed before? */
|
||||
HAS_FSYNCED_INODE, /* is the inode fsynced before? */
|
||||
HAS_LAST_FSYNC, /* has the latest node fsync mark? */
|
||||
IS_DIRTY, /* this nat entry is dirty? */
|
||||
};
|
||||
|
||||
/*
|
||||
* For node information
|
||||
*/
|
||||
|
@ -37,18 +51,11 @@ struct node_info {
|
|||
nid_t ino; /* inode number of the node's owner */
|
||||
block_t blk_addr; /* block address of the node */
|
||||
unsigned char version; /* version of the node */
|
||||
};
|
||||
|
||||
enum {
|
||||
IS_CHECKPOINTED, /* is it checkpointed before? */
|
||||
HAS_FSYNCED_INODE, /* is the inode fsynced before? */
|
||||
HAS_LAST_FSYNC, /* has the latest node fsync mark? */
|
||||
IS_DIRTY, /* this nat entry is dirty? */
|
||||
unsigned char flag; /* for node information bits */
|
||||
};
|
||||
|
||||
struct nat_entry {
|
||||
struct list_head list; /* for clean or dirty nat list */
|
||||
unsigned char flag; /* for node information bits */
|
||||
struct node_info ni; /* in-memory node information */
|
||||
};
|
||||
|
||||
|
@ -63,20 +70,30 @@ struct nat_entry {
|
|||
|
||||
#define inc_node_version(version) (++version)
|
||||
|
||||
static inline void copy_node_info(struct node_info *dst,
|
||||
struct node_info *src)
|
||||
{
|
||||
dst->nid = src->nid;
|
||||
dst->ino = src->ino;
|
||||
dst->blk_addr = src->blk_addr;
|
||||
dst->version = src->version;
|
||||
/* should not copy flag here */
|
||||
}
|
||||
|
||||
static inline void set_nat_flag(struct nat_entry *ne,
|
||||
unsigned int type, bool set)
|
||||
{
|
||||
unsigned char mask = 0x01 << type;
|
||||
if (set)
|
||||
ne->flag |= mask;
|
||||
ne->ni.flag |= mask;
|
||||
else
|
||||
ne->flag &= ~mask;
|
||||
ne->ni.flag &= ~mask;
|
||||
}
|
||||
|
||||
static inline bool get_nat_flag(struct nat_entry *ne, unsigned int type)
|
||||
{
|
||||
unsigned char mask = 0x01 << type;
|
||||
return ne->flag & mask;
|
||||
return ne->ni.flag & mask;
|
||||
}
|
||||
|
||||
static inline void nat_reset_flag(struct nat_entry *ne)
|
||||
|
@ -103,10 +120,19 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne,
|
|||
raw_ne->version = ni->version;
|
||||
}
|
||||
|
||||
static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return NM_I(sbi)->dirty_nat_cnt >= NM_I(sbi)->max_nid *
|
||||
NM_I(sbi)->dirty_nats_ratio / 100;
|
||||
}
|
||||
|
||||
enum mem_type {
|
||||
FREE_NIDS, /* indicates the free nid list */
|
||||
NAT_ENTRIES, /* indicates the cached nat entry */
|
||||
DIRTY_DENTS /* indicates dirty dentry pages */
|
||||
DIRTY_DENTS, /* indicates dirty dentry pages */
|
||||
INO_ENTRIES, /* indicates inode entries */
|
||||
EXTENT_CACHE, /* indicates extent cache */
|
||||
BASE_CHECK, /* check kernel status */
|
||||
};
|
||||
|
||||
struct nat_entry_set {
|
||||
|
@ -166,7 +192,7 @@ static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
|
|||
|
||||
block_addr = (pgoff_t)(nm_i->nat_blkaddr +
|
||||
(seg_off << sbi->log_blocks_per_seg << 1) +
|
||||
(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
|
||||
(block_off & (sbi->blocks_per_seg - 1)));
|
||||
|
||||
if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
|
||||
block_addr += sbi->blocks_per_seg;
|
||||
|
@ -192,21 +218,26 @@ 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);
|
||||
|
||||
if (f2fs_test_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);
|
||||
f2fs_change_bit(block_off, nm_i->nat_bitmap);
|
||||
}
|
||||
|
||||
static inline void fill_node_footer(struct page *page, nid_t nid,
|
||||
nid_t ino, unsigned int ofs, bool reset)
|
||||
{
|
||||
struct f2fs_node *rn = F2FS_NODE(page);
|
||||
unsigned int old_flag = 0;
|
||||
|
||||
if (reset)
|
||||
memset(rn, 0, sizeof(*rn));
|
||||
else
|
||||
old_flag = le32_to_cpu(rn->footer.flag);
|
||||
|
||||
rn->footer.nid = cpu_to_le32(nid);
|
||||
rn->footer.ino = cpu_to_le32(ino);
|
||||
rn->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT);
|
||||
|
||||
/* should remain old flag bits such as COLD_BIT_SHIFT */
|
||||
rn->footer.flag = cpu_to_le32((ofs << OFFSET_BIT_SHIFT) |
|
||||
(old_flag & OFFSET_BIT_MASK));
|
||||
}
|
||||
|
||||
static inline void copy_node_footer(struct page *dst, struct page *src)
|
||||
|
@ -295,17 +326,17 @@ static inline bool IS_DNODE(struct page *node_page)
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline void set_nid(struct page *p, int off, nid_t nid, bool i)
|
||||
static inline int set_nid(struct page *p, int off, nid_t nid, bool i)
|
||||
{
|
||||
struct f2fs_node *rn = F2FS_NODE(p);
|
||||
|
||||
f2fs_wait_on_page_writeback(p, NODE);
|
||||
f2fs_wait_on_page_writeback(p, NODE, true);
|
||||
|
||||
if (i)
|
||||
rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
|
||||
else
|
||||
rn->in.nid[off] = cpu_to_le32(nid);
|
||||
set_page_dirty(p);
|
||||
return set_page_dirty(p);
|
||||
}
|
||||
|
||||
static inline nid_t get_nid(struct page *p, int off, bool i)
|
||||
|
@ -323,28 +354,6 @@ static inline nid_t get_nid(struct page *p, int off, bool i)
|
|||
* - Mark cold node blocks in their node footer
|
||||
* - Mark cold data pages in page cache
|
||||
*/
|
||||
static inline int is_file(struct inode *inode, int type)
|
||||
{
|
||||
return F2FS_I(inode)->i_advise & type;
|
||||
}
|
||||
|
||||
static inline void set_file(struct inode *inode, int type)
|
||||
{
|
||||
F2FS_I(inode)->i_advise |= type;
|
||||
}
|
||||
|
||||
static inline void clear_file(struct inode *inode, int type)
|
||||
{
|
||||
F2FS_I(inode)->i_advise &= ~type;
|
||||
}
|
||||
|
||||
#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT)
|
||||
#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT)
|
||||
|
||||
static inline int is_cold_data(struct page *page)
|
||||
{
|
||||
return PageChecked(page);
|
||||
|
@ -370,6 +379,21 @@ static inline int is_node(struct page *page, int type)
|
|||
#define is_fsync_dnode(page) is_node(page, FSYNC_BIT_SHIFT)
|
||||
#define is_dent_dnode(page) is_node(page, DENT_BIT_SHIFT)
|
||||
|
||||
static inline int is_inline_node(struct page *page)
|
||||
{
|
||||
return PageChecked(page);
|
||||
}
|
||||
|
||||
static inline void set_inline_node(struct page *page)
|
||||
{
|
||||
SetPageChecked(page);
|
||||
}
|
||||
|
||||
static inline void clear_inline_node(struct page *page)
|
||||
{
|
||||
ClearPageChecked(page);
|
||||
}
|
||||
|
||||
static inline void set_cold_node(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct f2fs_node *rn = F2FS_NODE(page);
|
||||
|
|
|
@ -83,6 +83,11 @@ static int recover_dentry(struct inode *inode, struct page *ipage)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (file_enc_name(inode)) {
|
||||
iput(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
name.len = le32_to_cpu(raw_inode->i_namelen);
|
||||
name.name = raw_inode->i_name;
|
||||
|
||||
|
@ -93,10 +98,9 @@ static int recover_dentry(struct inode *inode, struct page *ipage)
|
|||
}
|
||||
retry:
|
||||
de = f2fs_find_entry(dir, &name, &page);
|
||||
if (de && inode->i_ino == le32_to_cpu(de->ino)) {
|
||||
clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
|
||||
if (de && inode->i_ino == le32_to_cpu(de->ino))
|
||||
goto out_unmap_put;
|
||||
}
|
||||
|
||||
if (de) {
|
||||
einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino));
|
||||
if (IS_ERR(einode)) {
|
||||
|
@ -111,11 +115,11 @@ retry:
|
|||
iput(einode);
|
||||
goto out_unmap_put;
|
||||
}
|
||||
f2fs_delete_entry(de, page, einode);
|
||||
f2fs_delete_entry(de, page, dir, einode);
|
||||
iput(einode);
|
||||
goto retry;
|
||||
}
|
||||
err = __f2fs_add_link(dir, &name, inode);
|
||||
err = __f2fs_add_link(dir, &name, inode, inode->i_ino, inode->i_mode);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
|
@ -129,7 +133,7 @@ retry:
|
|||
goto out;
|
||||
|
||||
out_unmap_put:
|
||||
kunmap(page);
|
||||
f2fs_dentry_kunmap(dir, page);
|
||||
f2fs_put_page(page, 0);
|
||||
out_err:
|
||||
iput(dir);
|
||||
|
@ -144,6 +148,7 @@ out:
|
|||
static void recover_inode(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct f2fs_inode *raw = F2FS_INODE(page);
|
||||
char *name;
|
||||
|
||||
inode->i_mode = le16_to_cpu(raw->i_mode);
|
||||
i_size_write(inode, le64_to_cpu(raw->i_size));
|
||||
|
@ -154,8 +159,39 @@ static void recover_inode(struct inode *inode, struct page *page)
|
|||
inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec);
|
||||
inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec);
|
||||
|
||||
if (file_enc_name(inode))
|
||||
name = "<encrypted>";
|
||||
else
|
||||
name = F2FS_INODE(page)->i_name;
|
||||
|
||||
f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s",
|
||||
ino_of_node(page), F2FS_INODE(page)->i_name);
|
||||
ino_of_node(page), name);
|
||||
}
|
||||
|
||||
static bool is_same_inode(struct inode *inode, struct page *ipage)
|
||||
{
|
||||
struct f2fs_inode *ri = F2FS_INODE(ipage);
|
||||
struct timespec disk;
|
||||
|
||||
if (!IS_INODE(ipage))
|
||||
return true;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_ctime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_ctime_nsec);
|
||||
if (timespec_compare(&inode->i_ctime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_atime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_atime_nsec);
|
||||
if (timespec_compare(&inode->i_atime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
disk.tv_sec = le64_to_cpu(ri->i_mtime);
|
||||
disk.tv_nsec = le32_to_cpu(ri->i_mtime_nsec);
|
||||
if (timespec_compare(&inode->i_mtime, &disk) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||
|
@ -170,13 +206,15 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
|||
curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
|
||||
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||
|
||||
ra_meta_pages(sbi, blkaddr, 1, META_POR, true);
|
||||
|
||||
while (1) {
|
||||
struct fsync_inode_entry *entry;
|
||||
|
||||
if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi))
|
||||
if (!is_valid_blkaddr(sbi, blkaddr, META_POR))
|
||||
return 0;
|
||||
|
||||
page = get_meta_page_ra(sbi, blkaddr);
|
||||
page = get_tmp_page(sbi, blkaddr);
|
||||
|
||||
if (cp_ver != cpver_of_node(page))
|
||||
break;
|
||||
|
@ -186,9 +224,8 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
|||
|
||||
entry = get_fsync_inode(head, ino_of_node(page));
|
||||
if (entry) {
|
||||
if (IS_INODE(page) && is_dent_dnode(page))
|
||||
set_inode_flag(F2FS_I(entry->inode),
|
||||
FI_INC_LINK);
|
||||
if (!is_same_inode(entry->inode, page))
|
||||
goto next;
|
||||
} else {
|
||||
if (IS_INODE(page) && is_dent_dnode(page)) {
|
||||
err = recover_inode_page(sbi, page);
|
||||
|
@ -210,8 +247,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
|
|||
if (IS_ERR(entry->inode)) {
|
||||
err = PTR_ERR(entry->inode);
|
||||
kmem_cache_free(fsync_entry_slab, entry);
|
||||
if (err == -ENOENT)
|
||||
if (err == -ENOENT) {
|
||||
err = 0;
|
||||
goto next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
list_add_tail(&entry->list, head);
|
||||
|
@ -227,6 +266,8 @@ next:
|
|||
/* check next segment */
|
||||
blkaddr = next_blkaddr_of_node(page);
|
||||
f2fs_put_page(page, 1);
|
||||
|
||||
ra_meta_pages_cond(sbi, blkaddr);
|
||||
}
|
||||
f2fs_put_page(page, 1);
|
||||
return err;
|
||||
|
@ -252,6 +293,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
|
|||
struct f2fs_summary_block *sum_node;
|
||||
struct f2fs_summary sum;
|
||||
struct page *sum_page, *node_page;
|
||||
struct dnode_of_data tdn = *dn;
|
||||
nid_t ino, nid;
|
||||
struct inode *inode;
|
||||
unsigned int offset;
|
||||
|
@ -279,17 +321,15 @@ got_it:
|
|||
/* Use the locked dnode page and inode */
|
||||
nid = le32_to_cpu(sum.nid);
|
||||
if (dn->inode->i_ino == nid) {
|
||||
struct dnode_of_data tdn = *dn;
|
||||
tdn.nid = nid;
|
||||
if (!dn->inode_page_locked)
|
||||
lock_page(dn->inode_page);
|
||||
tdn.node_page = dn->inode_page;
|
||||
tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node);
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
return 0;
|
||||
goto truncate_out;
|
||||
} else if (dn->nid == nid) {
|
||||
struct dnode_of_data tdn = *dn;
|
||||
tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node);
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
return 0;
|
||||
goto truncate_out;
|
||||
}
|
||||
|
||||
/* Get the node page */
|
||||
|
@ -310,38 +350,54 @@ got_it:
|
|||
inode = dn->inode;
|
||||
}
|
||||
|
||||
bidx = start_bidx_of_node(offset, F2FS_I(inode)) +
|
||||
le16_to_cpu(sum.ofs_in_node);
|
||||
bidx = start_bidx_of_node(offset, inode) + le16_to_cpu(sum.ofs_in_node);
|
||||
|
||||
if (ino != dn->inode->i_ino) {
|
||||
truncate_hole(inode, bidx, bidx + 1);
|
||||
/*
|
||||
* if inode page is locked, unlock temporarily, but its reference
|
||||
* count keeps alive.
|
||||
*/
|
||||
if (ino == dn->inode->i_ino && dn->inode_page_locked)
|
||||
unlock_page(dn->inode_page);
|
||||
|
||||
set_new_dnode(&tdn, inode, NULL, NULL, 0);
|
||||
if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE))
|
||||
goto out;
|
||||
|
||||
if (tdn.data_blkaddr == blkaddr)
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
|
||||
f2fs_put_dnode(&tdn);
|
||||
out:
|
||||
if (ino != dn->inode->i_ino)
|
||||
iput(inode);
|
||||
} else {
|
||||
struct dnode_of_data tdn;
|
||||
set_new_dnode(&tdn, inode, dn->inode_page, NULL, 0);
|
||||
if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE))
|
||||
return 0;
|
||||
if (tdn.data_blkaddr != NULL_ADDR)
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
f2fs_put_page(tdn.node_page, 1);
|
||||
}
|
||||
else if (dn->inode_page_locked)
|
||||
lock_page(dn->inode_page);
|
||||
return 0;
|
||||
|
||||
truncate_out:
|
||||
if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr)
|
||||
truncate_data_blocks_range(&tdn, 1);
|
||||
if (dn->inode->i_ino == nid && !dn->inode_page_locked)
|
||||
unlock_page(dn->inode_page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
struct page *page, block_t blkaddr)
|
||||
{
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
unsigned int start, end;
|
||||
struct dnode_of_data dn;
|
||||
struct f2fs_summary sum;
|
||||
struct node_info ni;
|
||||
unsigned int start, end;
|
||||
int err = 0, recovered = 0;
|
||||
|
||||
/* step 1: recover xattr */
|
||||
if (IS_INODE(page)) {
|
||||
recover_inline_xattr(inode, page);
|
||||
} else if (f2fs_has_xattr_block(ofs_of_node(page))) {
|
||||
/*
|
||||
* Deprecated; xattr blocks should be found from cold log.
|
||||
* But, we should remain this for backward compatibility.
|
||||
*/
|
||||
recover_xattr_data(inode, page, blkaddr);
|
||||
goto out;
|
||||
}
|
||||
|
@ -351,32 +407,51 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
|||
goto out;
|
||||
|
||||
/* step 3: recover data indices */
|
||||
start = start_bidx_of_node(ofs_of_node(page), fi);
|
||||
end = start + ADDRS_PER_PAGE(page, fi);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
start = start_bidx_of_node(ofs_of_node(page), inode);
|
||||
end = start + ADDRS_PER_PAGE(page, inode);
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
|
||||
err = get_dnode_of_data(&dn, start, ALLOC_NODE);
|
||||
if (err) {
|
||||
f2fs_unlock_op(sbi);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE);
|
||||
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
|
||||
|
||||
get_node_info(sbi, dn.nid, &ni);
|
||||
f2fs_bug_on(sbi, ni.ino != ino_of_node(page));
|
||||
f2fs_bug_on(sbi, ofs_of_node(dn.node_page) != ofs_of_node(page));
|
||||
|
||||
for (; start < end; start++) {
|
||||
for (; start < end; start++, dn.ofs_in_node++) {
|
||||
block_t src, dest;
|
||||
|
||||
src = datablock_addr(dn.node_page, dn.ofs_in_node);
|
||||
dest = datablock_addr(page, dn.ofs_in_node);
|
||||
|
||||
if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR) {
|
||||
/* skip recovering if dest is the same as src */
|
||||
if (src == dest)
|
||||
continue;
|
||||
|
||||
/* dest is invalid, just invalidate src block */
|
||||
if (dest == NULL_ADDR) {
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* dest is reserved block, invalidate src block
|
||||
* and then reserve one new block in dnode page.
|
||||
*/
|
||||
if (dest == NEW_ADDR) {
|
||||
truncate_data_blocks_range(&dn, 1);
|
||||
err = reserve_new_block(&dn);
|
||||
f2fs_bug_on(sbi, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* dest is valid block, try to recover from src to dest */
|
||||
if (is_valid_blkaddr(sbi, dest, META_POR)) {
|
||||
|
||||
if (src == NULL_ADDR) {
|
||||
err = reserve_new_block(&dn);
|
||||
/* We should not get -ENOSPC */
|
||||
|
@ -388,18 +463,13 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
|||
if (err)
|
||||
goto err;
|
||||
|
||||
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
|
||||
|
||||
/* write dummy data page */
|
||||
recover_data_page(sbi, NULL, &sum, src, dest);
|
||||
update_extent_cache(dest, &dn);
|
||||
f2fs_replace_block(sbi, &dn, src, dest,
|
||||
ni.version, false, false);
|
||||
recovered++;
|
||||
}
|
||||
dn.ofs_in_node++;
|
||||
}
|
||||
|
||||
/* write node page in place */
|
||||
set_summary(&sum, dn.nid, 0, 0);
|
||||
if (IS_INODE(dn.node_page))
|
||||
sync_inode_page(&dn);
|
||||
|
||||
|
@ -409,7 +479,6 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
|||
set_page_dirty(dn.node_page);
|
||||
err:
|
||||
f2fs_put_dnode(&dn);
|
||||
f2fs_unlock_op(sbi);
|
||||
out:
|
||||
f2fs_msg(sbi->sb, KERN_NOTICE,
|
||||
"recover_data: ino = %lx, recovered = %d blocks, err = %d",
|
||||
|
@ -417,8 +486,7 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int recover_data(struct f2fs_sb_info *sbi,
|
||||
struct list_head *head, int type)
|
||||
static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head)
|
||||
{
|
||||
unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
|
||||
struct curseg_info *curseg;
|
||||
|
@ -427,16 +495,18 @@ static int recover_data(struct f2fs_sb_info *sbi,
|
|||
block_t blkaddr;
|
||||
|
||||
/* get node pages in the current segment */
|
||||
curseg = CURSEG_I(sbi, type);
|
||||
curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
|
||||
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||
|
||||
while (1) {
|
||||
struct fsync_inode_entry *entry;
|
||||
|
||||
if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi))
|
||||
if (!is_valid_blkaddr(sbi, blkaddr, META_POR))
|
||||
break;
|
||||
|
||||
page = get_meta_page_ra(sbi, blkaddr);
|
||||
ra_meta_pages_cond(sbi, blkaddr);
|
||||
|
||||
page = get_tmp_page(sbi, blkaddr);
|
||||
|
||||
if (cp_ver != cpver_of_node(page)) {
|
||||
f2fs_put_page(page, 1);
|
||||
|
@ -496,14 +566,12 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
|
|||
|
||||
INIT_LIST_HEAD(&inode_list);
|
||||
|
||||
/* step #1: find fsynced inode numbers */
|
||||
sbi->por_doing = true;
|
||||
|
||||
/* prevent checkpoint */
|
||||
mutex_lock(&sbi->cp_mutex);
|
||||
|
||||
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||
|
||||
/* step #1: find fsynced inode numbers */
|
||||
err = find_fsync_dnodes(sbi, &inode_list);
|
||||
if (err)
|
||||
goto out;
|
||||
|
@ -514,7 +582,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
|
|||
need_writecp = true;
|
||||
|
||||
/* step #2: recover data */
|
||||
err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE);
|
||||
err = recover_data(sbi, &inode_list);
|
||||
if (!err)
|
||||
f2fs_bug_on(sbi, !list_empty(&inode_list));
|
||||
out:
|
||||
|
@ -523,28 +591,37 @@ out:
|
|||
|
||||
/* truncate meta pages to be used by the recovery */
|
||||
truncate_inode_pages_range(META_MAPPING(sbi),
|
||||
MAIN_BLKADDR(sbi) << PAGE_CACHE_SHIFT, -1);
|
||||
(loff_t)MAIN_BLKADDR(sbi) << PAGE_CACHE_SHIFT, -1);
|
||||
|
||||
if (err) {
|
||||
truncate_inode_pages_final(NODE_MAPPING(sbi));
|
||||
truncate_inode_pages_final(META_MAPPING(sbi));
|
||||
truncate_inode_pages(NODE_MAPPING(sbi), 0);
|
||||
truncate_inode_pages(META_MAPPING(sbi), 0);
|
||||
}
|
||||
|
||||
sbi->por_doing = false;
|
||||
clear_sbi_flag(sbi, SBI_POR_DOING);
|
||||
if (err) {
|
||||
discard_next_dnode(sbi, blkaddr);
|
||||
bool invalidate = false;
|
||||
|
||||
if (discard_next_dnode(sbi, blkaddr))
|
||||
invalidate = true;
|
||||
|
||||
/* Flush all the NAT/SIT pages */
|
||||
while (get_pages(sbi, F2FS_DIRTY_META))
|
||||
sync_meta_pages(sbi, META, LONG_MAX);
|
||||
|
||||
/* invalidate temporary meta page */
|
||||
if (invalidate)
|
||||
invalidate_mapping_pages(META_MAPPING(sbi),
|
||||
blkaddr, blkaddr);
|
||||
|
||||
set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG);
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
} else if (need_writecp) {
|
||||
struct cp_control cpc = {
|
||||
.reason = CP_SYNC,
|
||||
.reason = CP_RECOVERY,
|
||||
};
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
write_checkpoint(sbi, &cpc);
|
||||
err = write_checkpoint(sbi, &cpc);
|
||||
} else {
|
||||
mutex_unlock(&sbi->cp_mutex);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -136,10 +136,12 @@ enum {
|
|||
/*
|
||||
* BG_GC means the background cleaning job.
|
||||
* FG_GC means the on-demand cleaning job.
|
||||
* FORCE_FG_GC means on-demand cleaning job in background.
|
||||
*/
|
||||
enum {
|
||||
BG_GC = 0,
|
||||
FG_GC
|
||||
FG_GC,
|
||||
FORCE_FG_GC,
|
||||
};
|
||||
|
||||
/* for a function parameter to select a victim segment */
|
||||
|
@ -163,6 +165,7 @@ struct seg_entry {
|
|||
*/
|
||||
unsigned short ckpt_valid_blocks;
|
||||
unsigned char *ckpt_valid_map;
|
||||
unsigned char *discard_map;
|
||||
unsigned char type; /* segment type like CURSEG_XXX_TYPE */
|
||||
unsigned long long mtime; /* modification time of the segment */
|
||||
};
|
||||
|
@ -175,9 +178,19 @@ struct segment_allocation {
|
|||
void (*allocate_segment)(struct f2fs_sb_info *, int, bool);
|
||||
};
|
||||
|
||||
/*
|
||||
* this value is set in page as a private data which indicate that
|
||||
* the page is atomically written, and it is in inmem_pages list.
|
||||
*/
|
||||
#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1)
|
||||
|
||||
#define IS_ATOMIC_WRITTEN_PAGE(page) \
|
||||
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
|
||||
|
||||
struct inmem_pages {
|
||||
struct list_head list;
|
||||
struct page *page;
|
||||
block_t old_addr; /* for revoking when fail to commit */
|
||||
};
|
||||
|
||||
struct sit_info {
|
||||
|
@ -189,6 +202,7 @@ struct sit_info {
|
|||
char *sit_bitmap; /* SIT bitmap pointer */
|
||||
unsigned int bitmap_size; /* SIT bitmap size */
|
||||
|
||||
unsigned long *tmp_map; /* bitmap for temporal use */
|
||||
unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */
|
||||
unsigned int dirty_sentries; /* # of dirty sentries */
|
||||
unsigned int sents_per_block; /* # of SIT entries per block */
|
||||
|
@ -207,7 +221,7 @@ struct free_segmap_info {
|
|||
unsigned int start_segno; /* start segment number logically */
|
||||
unsigned int free_segments; /* # of free segments */
|
||||
unsigned int free_sections; /* # of free sections */
|
||||
rwlock_t segmap_lock; /* free segmap lock */
|
||||
spinlock_t segmap_lock; /* free segmap lock */
|
||||
unsigned long *free_segmap; /* free segment bitmap */
|
||||
unsigned long *free_secmap; /* free section bitmap */
|
||||
};
|
||||
|
@ -243,6 +257,8 @@ struct victim_selection {
|
|||
struct curseg_info {
|
||||
struct mutex curseg_mutex; /* lock for consistency */
|
||||
struct f2fs_summary_block *sum_blk; /* cached summary block */
|
||||
struct rw_semaphore journal_rwsem; /* protect journal area */
|
||||
struct f2fs_journal *journal; /* cached journal info */
|
||||
unsigned char alloc_type; /* current allocation type */
|
||||
unsigned int segno; /* current segment number */
|
||||
unsigned short next_blkoff; /* next block offset to write */
|
||||
|
@ -318,9 +334,9 @@ static inline unsigned int find_next_inuse(struct free_segmap_info *free_i,
|
|||
unsigned int max, unsigned int segno)
|
||||
{
|
||||
unsigned int ret;
|
||||
read_lock(&free_i->segmap_lock);
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
ret = find_next_bit(free_i->free_segmap, max, segno);
|
||||
read_unlock(&free_i->segmap_lock);
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -331,16 +347,17 @@ static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno)
|
|||
unsigned int start_segno = secno * sbi->segs_per_sec;
|
||||
unsigned int next;
|
||||
|
||||
write_lock(&free_i->segmap_lock);
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
clear_bit(segno, free_i->free_segmap);
|
||||
free_i->free_segments++;
|
||||
|
||||
next = find_next_bit(free_i->free_segmap, MAIN_SEGS(sbi), start_segno);
|
||||
next = find_next_bit(free_i->free_segmap,
|
||||
start_segno + sbi->segs_per_sec, start_segno);
|
||||
if (next >= start_segno + sbi->segs_per_sec) {
|
||||
clear_bit(secno, free_i->free_secmap);
|
||||
free_i->free_sections++;
|
||||
}
|
||||
write_unlock(&free_i->segmap_lock);
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
}
|
||||
|
||||
static inline void __set_inuse(struct f2fs_sb_info *sbi,
|
||||
|
@ -362,7 +379,7 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi,
|
|||
unsigned int start_segno = secno * sbi->segs_per_sec;
|
||||
unsigned int next;
|
||||
|
||||
write_lock(&free_i->segmap_lock);
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
if (test_and_clear_bit(segno, free_i->free_segmap)) {
|
||||
free_i->free_segments++;
|
||||
|
||||
|
@ -373,7 +390,7 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi,
|
|||
free_i->free_sections++;
|
||||
}
|
||||
}
|
||||
write_unlock(&free_i->segmap_lock);
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
}
|
||||
|
||||
static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi,
|
||||
|
@ -381,13 +398,13 @@ static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi,
|
|||
{
|
||||
struct free_segmap_info *free_i = FREE_I(sbi);
|
||||
unsigned int secno = segno / sbi->segs_per_sec;
|
||||
write_lock(&free_i->segmap_lock);
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
if (!test_and_set_bit(segno, free_i->free_segmap)) {
|
||||
free_i->free_segments--;
|
||||
if (!test_and_set_bit(secno, free_i->free_secmap))
|
||||
free_i->free_sections--;
|
||||
}
|
||||
write_unlock(&free_i->segmap_lock);
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
}
|
||||
|
||||
static inline void get_sit_bitmap(struct f2fs_sb_info *sbi,
|
||||
|
@ -460,7 +477,7 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed)
|
|||
int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
|
||||
int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
|
||||
|
||||
if (unlikely(sbi->por_doing))
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
return false;
|
||||
|
||||
return (free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs +
|
||||
|
@ -551,16 +568,15 @@ static inline unsigned short curseg_blkoff(struct f2fs_sb_info *sbi, int type)
|
|||
return curseg->next_blkoff;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
{
|
||||
BUG_ON(segno > TOTAL_SEGS(sbi) - 1);
|
||||
f2fs_bug_on(sbi, segno > TOTAL_SEGS(sbi) - 1);
|
||||
}
|
||||
|
||||
static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr)
|
||||
{
|
||||
BUG_ON(blk_addr < SEG0_BLKADDR(sbi));
|
||||
BUG_ON(blk_addr >= MAX_BLKADDR(sbi));
|
||||
f2fs_bug_on(sbi, blk_addr < SEG0_BLKADDR(sbi)
|
||||
|| blk_addr >= MAX_BLKADDR(sbi));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -569,16 +585,11 @@ static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr)
|
|||
static inline void check_block_count(struct f2fs_sb_info *sbi,
|
||||
int segno, struct f2fs_sit_entry *raw_sit)
|
||||
{
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
bool is_valid = test_bit_le(0, raw_sit->valid_map) ? true : false;
|
||||
int valid_blocks = 0;
|
||||
int cur_pos = 0, next_pos;
|
||||
|
||||
/* check segment usage */
|
||||
BUG_ON(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg);
|
||||
|
||||
/* check boundary of a given segment number */
|
||||
BUG_ON(segno > TOTAL_SEGS(sbi) - 1);
|
||||
|
||||
/* check bitmap with valid block count */
|
||||
do {
|
||||
if (is_valid) {
|
||||
|
@ -594,35 +605,11 @@ static inline void check_block_count(struct f2fs_sb_info *sbi,
|
|||
is_valid = !is_valid;
|
||||
} while (cur_pos < sbi->blocks_per_seg);
|
||||
BUG_ON(GET_SIT_VBLOCKS(raw_sit) != valid_blocks);
|
||||
}
|
||||
#else
|
||||
static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
{
|
||||
if (segno > TOTAL_SEGS(sbi) - 1)
|
||||
sbi->need_fsck = true;
|
||||
}
|
||||
|
||||
static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr)
|
||||
{
|
||||
if (blk_addr < SEG0_BLKADDR(sbi) || blk_addr >= MAX_BLKADDR(sbi))
|
||||
sbi->need_fsck = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Summary block is always treated as an invalid block
|
||||
*/
|
||||
static inline void check_block_count(struct f2fs_sb_info *sbi,
|
||||
int segno, struct f2fs_sit_entry *raw_sit)
|
||||
{
|
||||
/* check segment usage */
|
||||
if (GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg)
|
||||
sbi->need_fsck = true;
|
||||
|
||||
/* check boundary of a given segment number */
|
||||
if (segno > TOTAL_SEGS(sbi) - 1)
|
||||
sbi->need_fsck = true;
|
||||
}
|
||||
#endif
|
||||
/* check segment usage, and check boundary of a given segment number */
|
||||
f2fs_bug_on(sbi, GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg
|
||||
|| segno > TOTAL_SEGS(sbi) - 1);
|
||||
}
|
||||
|
||||
static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi,
|
||||
unsigned int start)
|
||||
|
@ -657,10 +644,7 @@ static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start)
|
|||
{
|
||||
unsigned int block_off = SIT_BLOCK_OFFSET(start);
|
||||
|
||||
if (f2fs_test_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);
|
||||
f2fs_change_bit(block_off, sit_i->sit_bitmap);
|
||||
}
|
||||
|
||||
static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi)
|
||||
|
@ -714,6 +698,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)
|
||||
{
|
||||
if (sbi->sb->s_bdi->dirty_exceeded)
|
||||
return 0;
|
||||
|
||||
if (type == DATA)
|
||||
return sbi->blocks_per_seg;
|
||||
else if (type == NODE)
|
||||
|
|
140
fs/f2fs/shrinker.c
Normal file
140
fs/f2fs/shrinker.c
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* f2fs shrinker support
|
||||
* the basic infra was copied from fs/ubifs/shrinker.c
|
||||
*
|
||||
* Copyright (c) 2015 Motorola Mobility
|
||||
* Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
|
||||
static LIST_HEAD(f2fs_list);
|
||||
static DEFINE_SPINLOCK(f2fs_list_lock);
|
||||
static unsigned int shrinker_run_no;
|
||||
|
||||
static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return NM_I(sbi)->nat_cnt - NM_I(sbi)->dirty_nat_cnt;
|
||||
}
|
||||
|
||||
static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (NM_I(sbi)->fcnt > NAT_ENTRY_PER_BLOCK)
|
||||
return NM_I(sbi)->fcnt - NAT_ENTRY_PER_BLOCK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return atomic_read(&sbi->total_zombie_tree) +
|
||||
atomic_read(&sbi->total_ext_node);
|
||||
}
|
||||
|
||||
unsigned long f2fs_shrink_count(struct shrinker *shrink,
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct list_head *p;
|
||||
unsigned long count = 0;
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
p = f2fs_list.next;
|
||||
while (p != &f2fs_list) {
|
||||
sbi = list_entry(p, struct f2fs_sb_info, s_list);
|
||||
|
||||
/* stop f2fs_put_super */
|
||||
if (!mutex_trylock(&sbi->umount_mutex)) {
|
||||
p = p->next;
|
||||
continue;
|
||||
}
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
|
||||
/* count extent cache entries */
|
||||
count += __count_extent_cache(sbi);
|
||||
|
||||
/* shrink clean nat cache entries */
|
||||
count += __count_nat_entries(sbi);
|
||||
|
||||
/* count free nids cache entries */
|
||||
count += __count_free_nids(sbi);
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
p = p->next;
|
||||
mutex_unlock(&sbi->umount_mutex);
|
||||
}
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
unsigned long f2fs_shrink_scan(struct shrinker *shrink,
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
unsigned long nr = sc->nr_to_scan;
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct list_head *p;
|
||||
unsigned int run_no;
|
||||
unsigned long freed = 0;
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
do {
|
||||
run_no = ++shrinker_run_no;
|
||||
} while (run_no == 0);
|
||||
p = f2fs_list.next;
|
||||
while (p != &f2fs_list) {
|
||||
sbi = list_entry(p, struct f2fs_sb_info, s_list);
|
||||
|
||||
if (sbi->shrinker_run_no == run_no)
|
||||
break;
|
||||
|
||||
/* stop f2fs_put_super */
|
||||
if (!mutex_trylock(&sbi->umount_mutex)) {
|
||||
p = p->next;
|
||||
continue;
|
||||
}
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
|
||||
sbi->shrinker_run_no = run_no;
|
||||
|
||||
/* shrink extent cache entries */
|
||||
freed += f2fs_shrink_extent_tree(sbi, nr >> 1);
|
||||
|
||||
/* shrink clean nat cache entries */
|
||||
if (freed < nr)
|
||||
freed += try_to_free_nats(sbi, nr - freed);
|
||||
|
||||
/* shrink free nids cache entries */
|
||||
if (freed < nr)
|
||||
freed += try_to_free_nids(sbi, nr - freed);
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
p = p->next;
|
||||
list_move_tail(&sbi->s_list, &f2fs_list);
|
||||
mutex_unlock(&sbi->umount_mutex);
|
||||
if (freed >= nr)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
return freed;
|
||||
}
|
||||
|
||||
void f2fs_join_shrinker(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
spin_lock(&f2fs_list_lock);
|
||||
list_add_tail(&sbi->s_list, &f2fs_list);
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
}
|
||||
|
||||
void f2fs_leave_shrinker(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
f2fs_shrink_extent_tree(sbi, __count_extent_cache(sbi));
|
||||
|
||||
spin_lock(&f2fs_list_lock);
|
||||
list_del(&sbi->s_list);
|
||||
spin_unlock(&f2fs_list_lock);
|
||||
}
|
690
fs/f2fs/super.c
690
fs/f2fs/super.c
File diff suppressed because it is too large
Load diff
161
fs/f2fs/trace.c
Normal file
161
fs/f2fs/trace.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* f2fs IO tracer
|
||||
*
|
||||
* Copyright (c) 2014 Motorola Mobility
|
||||
* Copyright (c) 2014 Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/radix-tree.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "trace.h"
|
||||
|
||||
static RADIX_TREE(pids, GFP_ATOMIC);
|
||||
static spinlock_t pids_lock;
|
||||
static struct last_io_info last_io;
|
||||
|
||||
static inline void __print_last_io(void)
|
||||
{
|
||||
if (!last_io.len)
|
||||
return;
|
||||
|
||||
trace_printk("%3x:%3x %4x %-16s %2x %5x %12x %4x\n",
|
||||
last_io.major, last_io.minor,
|
||||
last_io.pid, "----------------",
|
||||
last_io.type,
|
||||
last_io.fio.rw,
|
||||
last_io.fio.new_blkaddr,
|
||||
last_io.len);
|
||||
memset(&last_io, 0, sizeof(last_io));
|
||||
}
|
||||
|
||||
static int __file_type(struct inode *inode, pid_t pid)
|
||||
{
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
return __ATOMIC_FILE;
|
||||
else if (f2fs_is_volatile_file(inode))
|
||||
return __VOLATILE_FILE;
|
||||
else if (S_ISDIR(inode->i_mode))
|
||||
return __DIR_FILE;
|
||||
else if (inode->i_ino == F2FS_NODE_INO(F2FS_I_SB(inode)))
|
||||
return __NODE_FILE;
|
||||
else if (inode->i_ino == F2FS_META_INO(F2FS_I_SB(inode)))
|
||||
return __META_FILE;
|
||||
else if (pid)
|
||||
return __NORMAL_FILE;
|
||||
else
|
||||
return __MISC_FILE;
|
||||
}
|
||||
|
||||
void f2fs_trace_pid(struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
pid_t pid = task_pid_nr(current);
|
||||
void *p;
|
||||
|
||||
page->private = pid;
|
||||
|
||||
if (radix_tree_preload(GFP_NOFS))
|
||||
return;
|
||||
|
||||
spin_lock(&pids_lock);
|
||||
p = radix_tree_lookup(&pids, pid);
|
||||
if (p == current)
|
||||
goto out;
|
||||
if (p)
|
||||
radix_tree_delete(&pids, pid);
|
||||
|
||||
f2fs_radix_tree_insert(&pids, pid, current);
|
||||
|
||||
trace_printk("%3x:%3x %4x %-16s\n",
|
||||
MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev),
|
||||
pid, current->comm);
|
||||
out:
|
||||
spin_unlock(&pids_lock);
|
||||
radix_tree_preload_end();
|
||||
}
|
||||
|
||||
void f2fs_trace_ios(struct f2fs_io_info *fio, int flush)
|
||||
{
|
||||
struct inode *inode;
|
||||
pid_t pid;
|
||||
int major, minor;
|
||||
|
||||
if (flush) {
|
||||
__print_last_io();
|
||||
return;
|
||||
}
|
||||
|
||||
inode = fio->page->mapping->host;
|
||||
pid = page_private(fio->page);
|
||||
|
||||
major = MAJOR(inode->i_sb->s_dev);
|
||||
minor = MINOR(inode->i_sb->s_dev);
|
||||
|
||||
if (last_io.major == major && last_io.minor == minor &&
|
||||
last_io.pid == pid &&
|
||||
last_io.type == __file_type(inode, pid) &&
|
||||
last_io.fio.rw == fio->rw &&
|
||||
last_io.fio.new_blkaddr + last_io.len ==
|
||||
fio->new_blkaddr) {
|
||||
last_io.len++;
|
||||
return;
|
||||
}
|
||||
|
||||
__print_last_io();
|
||||
|
||||
last_io.major = major;
|
||||
last_io.minor = minor;
|
||||
last_io.pid = pid;
|
||||
last_io.type = __file_type(inode, pid);
|
||||
last_io.fio = *fio;
|
||||
last_io.len = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
void f2fs_build_trace_ios(void)
|
||||
{
|
||||
spin_lock_init(&pids_lock);
|
||||
}
|
||||
|
||||
#define PIDVEC_SIZE 128
|
||||
static unsigned int gang_lookup_pids(pid_t *results, unsigned long first_index,
|
||||
unsigned int max_items)
|
||||
{
|
||||
struct radix_tree_iter iter;
|
||||
void **slot;
|
||||
unsigned int ret = 0;
|
||||
|
||||
if (unlikely(!max_items))
|
||||
return 0;
|
||||
|
||||
radix_tree_for_each_slot(slot, &pids, &iter, first_index) {
|
||||
results[ret] = iter.index;
|
||||
if (++ret == PIDVEC_SIZE)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void f2fs_destroy_trace_ios(void)
|
||||
{
|
||||
pid_t pid[PIDVEC_SIZE];
|
||||
pid_t next_pid = 0;
|
||||
unsigned int found;
|
||||
|
||||
spin_lock(&pids_lock);
|
||||
while ((found = gang_lookup_pids(pid, next_pid, PIDVEC_SIZE))) {
|
||||
unsigned idx;
|
||||
|
||||
next_pid = pid[found - 1] + 1;
|
||||
for (idx = 0; idx < found; idx++)
|
||||
radix_tree_delete(&pids, pid[idx]);
|
||||
}
|
||||
spin_unlock(&pids_lock);
|
||||
}
|
46
fs/f2fs/trace.h
Normal file
46
fs/f2fs/trace.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* f2fs IO tracer
|
||||
*
|
||||
* Copyright (c) 2014 Motorola Mobility
|
||||
* Copyright (c) 2014 Jaegeuk Kim <jaegeuk@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __F2FS_TRACE_H__
|
||||
#define __F2FS_TRACE_H__
|
||||
|
||||
#ifdef CONFIG_F2FS_IO_TRACE
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
enum file_type {
|
||||
__NORMAL_FILE,
|
||||
__DIR_FILE,
|
||||
__NODE_FILE,
|
||||
__META_FILE,
|
||||
__ATOMIC_FILE,
|
||||
__VOLATILE_FILE,
|
||||
__MISC_FILE,
|
||||
};
|
||||
|
||||
struct last_io_info {
|
||||
int major, minor;
|
||||
pid_t pid;
|
||||
enum file_type type;
|
||||
struct f2fs_io_info fio;
|
||||
block_t len;
|
||||
};
|
||||
|
||||
extern void f2fs_trace_pid(struct page *);
|
||||
extern void f2fs_trace_ios(struct f2fs_io_info *, int);
|
||||
extern void f2fs_build_trace_ios(void);
|
||||
extern void f2fs_destroy_trace_ios(void);
|
||||
#else
|
||||
#define f2fs_trace_pid(p)
|
||||
#define f2fs_trace_ios(i, n)
|
||||
#define f2fs_build_trace_ios()
|
||||
#define f2fs_destroy_trace_ios()
|
||||
|
||||
#endif
|
||||
#endif /* __F2FS_TRACE_H__ */
|
|
@ -83,7 +83,7 @@ static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name,
|
|||
}
|
||||
if (strcmp(name, "") == 0)
|
||||
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,
|
||||
|
@ -135,7 +135,8 @@ static int f2fs_xattr_advise_get(struct dentry *dentry, const char *name,
|
|||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*((char *)buffer) = F2FS_I(inode)->i_advise;
|
||||
if (buffer)
|
||||
*((char *)buffer) = F2FS_I(inode)->i_advise;
|
||||
return sizeof(char);
|
||||
}
|
||||
|
||||
|
@ -152,6 +153,7 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name,
|
|||
return -EINVAL;
|
||||
|
||||
F2FS_I(inode)->i_advise |= *(char *)value;
|
||||
mark_inode_dirty(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -345,7 +347,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
|
||||
if (ipage) {
|
||||
inline_addr = inline_xattr_addr(ipage);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true);
|
||||
} else {
|
||||
page = get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(page)) {
|
||||
|
@ -353,7 +355,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
return PTR_ERR(page);
|
||||
}
|
||||
inline_addr = inline_xattr_addr(page);
|
||||
f2fs_wait_on_page_writeback(page, NODE);
|
||||
f2fs_wait_on_page_writeback(page, NODE, true);
|
||||
}
|
||||
memcpy(inline_addr, txattr_addr, inline_size);
|
||||
f2fs_put_page(page, 1);
|
||||
|
@ -374,7 +376,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
return PTR_ERR(xpage);
|
||||
}
|
||||
f2fs_bug_on(sbi, new_nid);
|
||||
f2fs_wait_on_page_writeback(xpage, NODE);
|
||||
f2fs_wait_on_page_writeback(xpage, NODE, true);
|
||||
} else {
|
||||
struct dnode_of_data dn;
|
||||
set_new_dnode(&dn, inode, NULL, NULL, new_nid);
|
||||
|
@ -398,7 +400,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
|
|||
}
|
||||
|
||||
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;
|
||||
void *base_addr;
|
||||
|
@ -412,7 +414,7 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
|
|||
if (len > F2FS_NAME_LEN)
|
||||
return -ERANGE;
|
||||
|
||||
base_addr = read_all_xattrs(inode, NULL);
|
||||
base_addr = read_all_xattrs(inode, ipage);
|
||||
if (!base_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -497,9 +499,12 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
|
||||
len = strlen(name);
|
||||
|
||||
if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN(inode))
|
||||
if (len > F2FS_NAME_LEN)
|
||||
return -ERANGE;
|
||||
|
||||
if (size > MAX_VALUE_LEN(inode))
|
||||
return -E2BIG;
|
||||
|
||||
base_addr = read_all_xattrs(inode, ipage);
|
||||
if (!base_addr)
|
||||
goto exit;
|
||||
|
@ -582,6 +587,9 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
|||
inode->i_ctime = CURRENT_TIME;
|
||||
clear_inode_flag(fi, FI_ACL_MODE);
|
||||
}
|
||||
if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
|
||||
!strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
|
||||
f2fs_set_encrypted_inode(inode);
|
||||
|
||||
if (ipage)
|
||||
update_inode(inode, ipage);
|
||||
|
@ -603,7 +611,7 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
|
|||
if (ipage)
|
||||
return __f2fs_setxattr(inode, index, name, value,
|
||||
size, ipage, flags);
|
||||
f2fs_balance_fs(sbi);
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
/* protect xattr_ver */
|
||||
|
@ -612,5 +620,6 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
|
|||
up_write(&F2FS_I(inode)->i_sem);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
f2fs_update_time(sbi, REQ_TIME);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
#define F2FS_XATTR_INDEX_LUSTRE 5
|
||||
#define F2FS_XATTR_INDEX_SECURITY 6
|
||||
#define F2FS_XATTR_INDEX_ADVISE 7
|
||||
/* Should be same as EXT4_XATTR_INDEX_ENCRYPTION */
|
||||
#define F2FS_XATTR_INDEX_ENCRYPTION 9
|
||||
|
||||
#define F2FS_XATTR_NAME_ENCRYPTION_CONTEXT "c"
|
||||
|
||||
struct f2fs_xattr_header {
|
||||
__le32 h_magic; /* magic number for identification */
|
||||
|
@ -115,18 +119,21 @@ extern const struct xattr_handler *f2fs_xattr_handlers[];
|
|||
|
||||
extern int f2fs_setxattr(struct inode *, int, const char *,
|
||||
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);
|
||||
#else
|
||||
|
||||
#define f2fs_xattr_handlers NULL
|
||||
static inline int f2fs_setxattr(struct inode *inode, int index,
|
||||
const char *name, const void *value, size_t size, int flags)
|
||||
const char *name, const void *value, size_t size,
|
||||
struct page *page, int flags)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -19,12 +19,16 @@
|
|||
#define F2FS_MAX_LOG_SECTOR_SIZE 12 /* 12 bits for 4096 bytes */
|
||||
#define F2FS_LOG_SECTORS_PER_BLOCK 3 /* log number for sector/blk */
|
||||
#define F2FS_BLKSIZE 4096 /* support only 4KB block */
|
||||
#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */
|
||||
#define F2FS_MAX_EXTENSION 64 /* # of extension entries */
|
||||
#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE)
|
||||
#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) >> F2FS_BLKSIZE_BITS)
|
||||
|
||||
#define NULL_ADDR ((block_t)0) /* used as block_t addresses */
|
||||
#define NEW_ADDR ((block_t)-1) /* used as block_t addresses */
|
||||
|
||||
#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS)
|
||||
#define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS)
|
||||
|
||||
/* 0, 1(node nid), 2(meta nid) are reserved node id */
|
||||
#define F2FS_RESERVED_NODE_NUM 3
|
||||
|
||||
|
@ -33,7 +37,8 @@
|
|||
#define F2FS_META_INO(sbi) (sbi->meta_ino_num)
|
||||
|
||||
/* 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
|
||||
|
@ -45,6 +50,9 @@
|
|||
#define MAX_ACTIVE_NODE_LOGS 8
|
||||
#define MAX_ACTIVE_DATA_LOGS 8
|
||||
|
||||
#define VERSION_LEN 256
|
||||
#define MAX_VOLUME_NAME 512
|
||||
|
||||
/*
|
||||
* For superblock
|
||||
*/
|
||||
|
@ -77,15 +85,22 @@ struct f2fs_super_block {
|
|||
__le32 node_ino; /* node inode number */
|
||||
__le32 meta_ino; /* meta inode number */
|
||||
__u8 uuid[16]; /* 128-bit uuid for volume */
|
||||
__le16 volume_name[512]; /* volume name */
|
||||
__le16 volume_name[MAX_VOLUME_NAME]; /* volume name */
|
||||
__le32 extension_count; /* # of extensions below */
|
||||
__u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */
|
||||
__le32 cp_payload;
|
||||
__u8 version[VERSION_LEN]; /* the kernel version */
|
||||
__u8 init_version[VERSION_LEN]; /* the initial kernel version */
|
||||
__le32 feature; /* defined features */
|
||||
__u8 encryption_level; /* versioning level for encryption */
|
||||
__u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
|
||||
__u8 reserved[871]; /* valid reserved region */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* For checkpoint
|
||||
*/
|
||||
#define CP_FASTBOOT_FLAG 0x00000020
|
||||
#define CP_FSCK_FLAG 0x00000010
|
||||
#define CP_ERROR_FLAG 0x00000008
|
||||
#define CP_COMPACT_SUM_FLAG 0x00000004
|
||||
|
@ -147,7 +162,7 @@ struct f2fs_orphan_block {
|
|||
*/
|
||||
struct f2fs_extent {
|
||||
__le32 fofs; /* start file offset of the extent */
|
||||
__le32 blk_addr; /* start block address of the extent */
|
||||
__le32 blk; /* start block address of the extent */
|
||||
__le32 len; /* lengh of the extent */
|
||||
} __packed;
|
||||
|
||||
|
@ -155,12 +170,12 @@ struct f2fs_extent {
|
|||
#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */
|
||||
#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */
|
||||
#define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */
|
||||
#define ADDRS_PER_INODE(fi) addrs_per_inode(fi)
|
||||
#define ADDRS_PER_INODE(inode) addrs_per_inode(inode)
|
||||
#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */
|
||||
#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */
|
||||
|
||||
#define ADDRS_PER_PAGE(page, fi) \
|
||||
(IS_INODE(page) ? ADDRS_PER_INODE(fi) : ADDRS_PER_BLOCK)
|
||||
#define ADDRS_PER_PAGE(page, inode) \
|
||||
(IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK)
|
||||
|
||||
#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1)
|
||||
#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2)
|
||||
|
@ -170,14 +185,13 @@ struct f2fs_extent {
|
|||
|
||||
#define F2FS_INLINE_XATTR 0x01 /* file inline xattr 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 F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */
|
||||
|
||||
#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
|
||||
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 {
|
||||
__le16 i_mode; /* file mode */
|
||||
__u8 i_advise; /* file hints */
|
||||
|
@ -225,6 +239,8 @@ enum {
|
|||
OFFSET_BIT_SHIFT
|
||||
};
|
||||
|
||||
#define OFFSET_BIT_MASK (0x07) /* (0x01 << OFFSET_BIT_SHIFT) - 1 */
|
||||
|
||||
struct node_footer {
|
||||
__le32 nid; /* node id */
|
||||
__le32 ino; /* inode nunmber */
|
||||
|
@ -329,7 +345,7 @@ struct f2fs_summary {
|
|||
|
||||
struct summary_footer {
|
||||
unsigned char entry_type; /* SUM_TYPE_XXX */
|
||||
__u32 check_sum; /* summary checksum */
|
||||
__le32 check_sum; /* summary checksum */
|
||||
} __packed;
|
||||
|
||||
#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\
|
||||
|
@ -342,6 +358,12 @@ struct summary_footer {
|
|||
sizeof(struct sit_journal_entry))
|
||||
#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\
|
||||
sizeof(struct sit_journal_entry))
|
||||
|
||||
/* Reserved area should make size of f2fs_extra_info equals to
|
||||
* that of nat_journal and sit_journal.
|
||||
*/
|
||||
#define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8)
|
||||
|
||||
/*
|
||||
* frequently updated NAT/SIT entries can be stored in the spare area in
|
||||
* summary blocks
|
||||
|
@ -371,18 +393,28 @@ struct sit_journal {
|
|||
__u8 reserved[SIT_JOURNAL_RESERVED];
|
||||
} __packed;
|
||||
|
||||
/* 4KB-sized summary block structure */
|
||||
struct f2fs_summary_block {
|
||||
struct f2fs_summary entries[ENTRIES_IN_SUM];
|
||||
struct f2fs_extra_info {
|
||||
__le64 kbytes_written;
|
||||
__u8 reserved[EXTRA_INFO_RESERVED];
|
||||
} __packed;
|
||||
|
||||
struct f2fs_journal {
|
||||
union {
|
||||
__le16 n_nats;
|
||||
__le16 n_sits;
|
||||
};
|
||||
/* spare area is used by NAT or SIT journals */
|
||||
/* spare area is used by NAT or SIT journals or extra info */
|
||||
union {
|
||||
struct nat_journal nat_j;
|
||||
struct sit_journal sit_j;
|
||||
struct f2fs_extra_info info;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
/* 4KB-sized summary block structure */
|
||||
struct f2fs_summary_block {
|
||||
struct f2fs_summary entries[ENTRIES_IN_SUM];
|
||||
struct f2fs_journal journal;
|
||||
struct summary_footer footer;
|
||||
} __packed;
|
||||
|
||||
|
@ -402,15 +434,25 @@ typedef __le32 f2fs_hash_t;
|
|||
|
||||
#define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS)
|
||||
|
||||
/* the number of dentry in a block */
|
||||
#define NR_DENTRY_IN_BLOCK 214
|
||||
|
||||
/* MAX level for dir lookup */
|
||||
#define MAX_DIR_HASH_DEPTH 63
|
||||
|
||||
/* MAX buckets in one level of dir */
|
||||
#define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1))
|
||||
|
||||
/*
|
||||
* space utilization of regular dentry and inline dentry
|
||||
* regular dentry inline dentry
|
||||
* bitmap 1 * 27 = 27 1 * 23 = 23
|
||||
* reserved 1 * 3 = 3 1 * 7 = 7
|
||||
* dentry 11 * 214 = 2354 11 * 182 = 2002
|
||||
* filename 8 * 214 = 1712 8 * 182 = 1456
|
||||
* total 4096 3488
|
||||
*
|
||||
* Note: there are more reserved space in inline dentry than in regular
|
||||
* dentry, when converting inline dentry we should handle this carefully.
|
||||
*/
|
||||
#define NR_DENTRY_IN_BLOCK 214 /* the number of dentry in a block */
|
||||
#define SIZE_OF_DIR_ENTRY 11 /* by byte */
|
||||
#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \
|
||||
BITS_PER_BYTE)
|
||||
|
@ -435,6 +477,24 @@ struct f2fs_dentry_block {
|
|||
__u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
|
||||
} __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 */
|
||||
enum {
|
||||
F2FS_FT_UNKNOWN,
|
||||
|
|
|
@ -14,7 +14,12 @@
|
|||
{ NODE, "NODE" }, \
|
||||
{ DATA, "DATA" }, \
|
||||
{ META, "META" }, \
|
||||
{ META_FLUSH, "META_FLUSH" })
|
||||
{ META_FLUSH, "META_FLUSH" }, \
|
||||
{ INMEM, "INMEM" }, \
|
||||
{ INMEM_DROP, "INMEM_DROP" }, \
|
||||
{ INMEM_REVOKE, "INMEM_REVOKE" }, \
|
||||
{ IPU, "IN-PLACE" }, \
|
||||
{ OPU, "OUT-OF-PLACE" })
|
||||
|
||||
#define F2FS_BIO_MASK(t) (t & (READA | WRITE_FLUSH_FUA))
|
||||
#define F2FS_BIO_EXTRA_MASK(t) (t & (REQ_META | REQ_PRIO))
|
||||
|
@ -72,10 +77,13 @@
|
|||
#define show_cpreason(type) \
|
||||
__print_symbolic(type, \
|
||||
{ CP_UMOUNT, "Umount" }, \
|
||||
{ CP_FASTBOOT, "Fastboot" }, \
|
||||
{ CP_SYNC, "Sync" }, \
|
||||
{ CP_RECOVERY, "Recovery" }, \
|
||||
{ CP_DISCARD, "Discard" })
|
||||
|
||||
struct victim_sel_policy;
|
||||
struct f2fs_map_blocks;
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs__inode,
|
||||
|
||||
|
@ -148,14 +156,14 @@ DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter,
|
|||
|
||||
TRACE_EVENT(f2fs_sync_file_exit,
|
||||
|
||||
TP_PROTO(struct inode *inode, bool need_cp, int datasync, int ret),
|
||||
TP_PROTO(struct inode *inode, int need_cp, int datasync, int ret),
|
||||
|
||||
TP_ARGS(inode, need_cp, datasync, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(bool, need_cp)
|
||||
__field(int, need_cp)
|
||||
__field(int, datasync)
|
||||
__field(int, ret)
|
||||
),
|
||||
|
@ -190,7 +198,7 @@ TRACE_EVENT(f2fs_sync_fs,
|
|||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->dirty = F2FS_SB(sb)->s_dirty;
|
||||
__entry->dirty = is_sbi_flag_set(F2FS_SB(sb), SBI_IS_DIRTY);
|
||||
__entry->wait = wait;
|
||||
),
|
||||
|
||||
|
@ -440,71 +448,66 @@ TRACE_EVENT(f2fs_truncate_partial_nodes,
|
|||
__entry->err)
|
||||
);
|
||||
|
||||
TRACE_EVENT_CONDITION(f2fs_submit_page_bio,
|
||||
TRACE_EVENT(f2fs_map_blocks,
|
||||
TP_PROTO(struct inode *inode, struct f2fs_map_blocks *map, int ret),
|
||||
|
||||
TP_PROTO(struct page *page, sector_t blkaddr, int type),
|
||||
|
||||
TP_ARGS(page, blkaddr, type),
|
||||
|
||||
TP_CONDITION(page->mapping),
|
||||
TP_ARGS(inode, map, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(pgoff_t, index)
|
||||
__field(sector_t, blkaddr)
|
||||
__field(int, type)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = page->mapping->host->i_sb->s_dev;
|
||||
__entry->ino = page->mapping->host->i_ino;
|
||||
__entry->index = page->index;
|
||||
__entry->blkaddr = blkaddr;
|
||||
__entry->type = type;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
|
||||
"blkaddr = 0x%llx, bio_type = %s%s",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long)__entry->index,
|
||||
(unsigned long long)__entry->blkaddr,
|
||||
show_bio_type(__entry->type))
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_get_data_block,
|
||||
TP_PROTO(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh, int ret),
|
||||
|
||||
TP_ARGS(inode, iblock, bh, ret),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(sector_t, iblock)
|
||||
__field(sector_t, bh_start)
|
||||
__field(size_t, bh_size)
|
||||
__field(block_t, m_lblk)
|
||||
__field(block_t, m_pblk)
|
||||
__field(unsigned int, m_len)
|
||||
__field(int, ret)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->iblock = iblock;
|
||||
__entry->bh_start = bh->b_blocknr;
|
||||
__entry->bh_size = bh->b_size;
|
||||
__entry->m_lblk = map->m_lblk;
|
||||
__entry->m_pblk = map->m_pblk;
|
||||
__entry->m_len = map->m_len;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, file offset = %llu, "
|
||||
"start blkaddr = 0x%llx, len = 0x%llx bytes, err = %d",
|
||||
"start blkaddr = 0x%llx, len = 0x%llx, err = %d",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long long)__entry->iblock,
|
||||
(unsigned long long)__entry->bh_start,
|
||||
(unsigned long long)__entry->bh_size,
|
||||
(unsigned long long)__entry->m_lblk,
|
||||
(unsigned long long)__entry->m_pblk,
|
||||
(unsigned long long)__entry->m_len,
|
||||
__entry->ret)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_background_gc,
|
||||
|
||||
TP_PROTO(struct super_block *sb, long wait_ms,
|
||||
unsigned int prefree, unsigned int free),
|
||||
|
||||
TP_ARGS(sb, wait_ms, prefree, free),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(long, wait_ms)
|
||||
__field(unsigned int, prefree)
|
||||
__field(unsigned int, free)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->wait_ms = wait_ms;
|
||||
__entry->prefree = prefree;
|
||||
__entry->free = free;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), wait_ms = %ld, prefree = %u, free = %u",
|
||||
show_dev(__entry),
|
||||
__entry->wait_ms,
|
||||
__entry->prefree,
|
||||
__entry->free)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_get_victim,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, int gc_type,
|
||||
|
@ -680,11 +683,66 @@ TRACE_EVENT(f2fs_reserve_new_block,
|
|||
__entry->ofs_in_node)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs__submit_page_bio,
|
||||
|
||||
TP_PROTO(struct page *page, struct f2fs_io_info *fio),
|
||||
|
||||
TP_ARGS(page, fio),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(pgoff_t, index)
|
||||
__field(block_t, old_blkaddr)
|
||||
__field(block_t, new_blkaddr)
|
||||
__field(int, rw)
|
||||
__field(int, type)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = page->mapping->host->i_sb->s_dev;
|
||||
__entry->ino = page->mapping->host->i_ino;
|
||||
__entry->index = page->index;
|
||||
__entry->old_blkaddr = fio->old_blkaddr;
|
||||
__entry->new_blkaddr = fio->new_blkaddr;
|
||||
__entry->rw = fio->rw;
|
||||
__entry->type = fio->type;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, "
|
||||
"oldaddr = 0x%llx, newaddr = 0x%llx rw = %s%s, type = %s",
|
||||
show_dev_ino(__entry),
|
||||
(unsigned long)__entry->index,
|
||||
(unsigned long long)__entry->old_blkaddr,
|
||||
(unsigned long long)__entry->new_blkaddr,
|
||||
show_bio_type(__entry->rw),
|
||||
show_block_type(__entry->type))
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_bio,
|
||||
|
||||
TP_PROTO(struct page *page, struct f2fs_io_info *fio),
|
||||
|
||||
TP_ARGS(page, fio),
|
||||
|
||||
TP_CONDITION(page->mapping)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_mbio,
|
||||
|
||||
TP_PROTO(struct page *page, struct f2fs_io_info *fio),
|
||||
|
||||
TP_ARGS(page, fio),
|
||||
|
||||
TP_CONDITION(page->mapping)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs__submit_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, rw, type, bio),
|
||||
TP_ARGS(sb, fio, bio),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
|
@ -696,8 +754,8 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio,
|
|||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->rw = rw;
|
||||
__entry->type = type;
|
||||
__entry->rw = fio->rw;
|
||||
__entry->type = fio->type;
|
||||
__entry->sector = bio->bi_iter.bi_sector;
|
||||
__entry->size = bio->bi_iter.bi_size;
|
||||
),
|
||||
|
@ -712,18 +770,20 @@ DECLARE_EVENT_CLASS(f2fs__submit_bio,
|
|||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_write_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, rw, type, bio),
|
||||
TP_ARGS(sb, fio, bio),
|
||||
|
||||
TP_CONDITION(bio)
|
||||
);
|
||||
|
||||
DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_read_bio,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int rw, int type, struct bio *bio),
|
||||
TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio,
|
||||
struct bio *bio),
|
||||
|
||||
TP_ARGS(sb, rw, type, bio),
|
||||
TP_ARGS(sb, fio, bio),
|
||||
|
||||
TP_CONDITION(bio)
|
||||
);
|
||||
|
@ -831,6 +891,13 @@ DEFINE_EVENT(f2fs__page, f2fs_writepage,
|
|||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__page, f2fs_do_write_data_page,
|
||||
|
||||
TP_PROTO(struct page *page, int type),
|
||||
|
||||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__page, f2fs_readpage,
|
||||
|
||||
TP_PROTO(struct page *page, int type),
|
||||
|
@ -852,6 +919,20 @@ DEFINE_EVENT(f2fs__page, f2fs_vm_page_mkwrite,
|
|||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__page, f2fs_register_inmem_page,
|
||||
|
||||
TP_PROTO(struct page *page, int type),
|
||||
|
||||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs__page, f2fs_commit_inmem_page,
|
||||
|
||||
TP_PROTO(struct page *page, int type),
|
||||
|
||||
TP_ARGS(page, type)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_writepages,
|
||||
|
||||
TP_PROTO(struct inode *inode, struct writeback_control *wbc, int type),
|
||||
|
@ -916,36 +997,30 @@ TRACE_EVENT(f2fs_writepages,
|
|||
__entry->for_sync)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_submit_page_mbio,
|
||||
TRACE_EVENT(f2fs_readpages,
|
||||
|
||||
TP_PROTO(struct page *page, int rw, int type, block_t blk_addr),
|
||||
TP_PROTO(struct inode *inode, struct page *page, unsigned int nrpage),
|
||||
|
||||
TP_ARGS(page, rw, type, blk_addr),
|
||||
TP_ARGS(inode, page, nrpage),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(int, rw)
|
||||
__field(int, type)
|
||||
__field(pgoff_t, index)
|
||||
__field(block_t, block)
|
||||
__field(pgoff_t, start)
|
||||
__field(unsigned int, nrpage)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = page->mapping->host->i_sb->s_dev;
|
||||
__entry->ino = page->mapping->host->i_ino;
|
||||
__entry->rw = rw;
|
||||
__entry->type = type;
|
||||
__entry->index = page->index;
|
||||
__entry->block = blk_addr;
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->start = page->index;
|
||||
__entry->nrpage = nrpage;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, %s%s, %s, index = %lu, blkaddr = 0x%llx",
|
||||
TP_printk("dev = (%d,%d), ino = %lu, start = %lu nrpage = %u",
|
||||
show_dev_ino(__entry),
|
||||
show_bio_type(__entry->rw),
|
||||
show_block_type(__entry->type),
|
||||
(unsigned long)__entry->index,
|
||||
(unsigned long long)__entry->block)
|
||||
(unsigned long)__entry->start,
|
||||
__entry->nrpage)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_write_checkpoint,
|
||||
|
@ -998,14 +1073,15 @@ TRACE_EVENT(f2fs_issue_discard,
|
|||
|
||||
TRACE_EVENT(f2fs_issue_flush,
|
||||
|
||||
TP_PROTO(struct super_block *sb, bool nobarrier, bool flush_merge),
|
||||
TP_PROTO(struct super_block *sb, unsigned int nobarrier,
|
||||
unsigned int flush_merge),
|
||||
|
||||
TP_ARGS(sb, nobarrier, flush_merge),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(bool, nobarrier)
|
||||
__field(bool, flush_merge)
|
||||
__field(unsigned int, nobarrier)
|
||||
__field(unsigned int, flush_merge)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
|
@ -1019,6 +1095,183 @@ TRACE_EVENT(f2fs_issue_flush,
|
|||
__entry->nobarrier ? "skip (nobarrier)" : "issue",
|
||||
__entry->flush_merge ? " with flush_merge" : "")
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_lookup_extent_tree_start,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned int pgofs),
|
||||
|
||||
TP_ARGS(inode, pgofs),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned int, pgofs)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->pgofs = pgofs;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u",
|
||||
show_dev_ino(__entry),
|
||||
__entry->pgofs)
|
||||
);
|
||||
|
||||
TRACE_EVENT_CONDITION(f2fs_lookup_extent_tree_end,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned int pgofs,
|
||||
struct extent_info *ei),
|
||||
|
||||
TP_ARGS(inode, pgofs, ei),
|
||||
|
||||
TP_CONDITION(ei),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned int, pgofs)
|
||||
__field(unsigned int, fofs)
|
||||
__field(u32, blk)
|
||||
__field(unsigned int, len)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->pgofs = pgofs;
|
||||
__entry->fofs = ei->fofs;
|
||||
__entry->blk = ei->blk;
|
||||
__entry->len = ei->len;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, "
|
||||
"ext_info(fofs: %u, blk: %u, len: %u)",
|
||||
show_dev_ino(__entry),
|
||||
__entry->pgofs,
|
||||
__entry->fofs,
|
||||
__entry->blk,
|
||||
__entry->len)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_update_extent_tree_range,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned int pgofs, block_t blkaddr,
|
||||
unsigned int len),
|
||||
|
||||
TP_ARGS(inode, pgofs, blkaddr, len),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned int, pgofs)
|
||||
__field(u32, blk)
|
||||
__field(unsigned int, len)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->pgofs = pgofs;
|
||||
__entry->blk = blkaddr;
|
||||
__entry->len = len;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, "
|
||||
"blkaddr = %u, len = %u",
|
||||
show_dev_ino(__entry),
|
||||
__entry->pgofs,
|
||||
__entry->blk,
|
||||
__entry->len)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_shrink_extent_tree,
|
||||
|
||||
TP_PROTO(struct f2fs_sb_info *sbi, unsigned int node_cnt,
|
||||
unsigned int tree_cnt),
|
||||
|
||||
TP_ARGS(sbi, node_cnt, tree_cnt),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(unsigned int, node_cnt)
|
||||
__field(unsigned int, tree_cnt)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sbi->sb->s_dev;
|
||||
__entry->node_cnt = node_cnt;
|
||||
__entry->tree_cnt = tree_cnt;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), shrunk: node_cnt = %u, tree_cnt = %u",
|
||||
show_dev(__entry),
|
||||
__entry->node_cnt,
|
||||
__entry->tree_cnt)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_destroy_extent_tree,
|
||||
|
||||
TP_PROTO(struct inode *inode, unsigned int node_cnt),
|
||||
|
||||
TP_ARGS(inode, node_cnt),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned int, node_cnt)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = inode->i_sb->s_dev;
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->node_cnt = node_cnt;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), ino = %lu, destroyed: node_cnt = %u",
|
||||
show_dev_ino(__entry),
|
||||
__entry->node_cnt)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, int count),
|
||||
|
||||
TP_ARGS(sb, type, count),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(int, type)
|
||||
__field(int, count)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->type = type;
|
||||
__entry->count = count;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), %s, dirty count = %d",
|
||||
show_dev(__entry),
|
||||
show_file_type(__entry->type),
|
||||
__entry->count)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_enter,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, int count),
|
||||
|
||||
TP_ARGS(sb, type, count)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_exit,
|
||||
|
||||
TP_PROTO(struct super_block *sb, int type, int count),
|
||||
|
||||
TP_ARGS(sb, type, count)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_F2FS_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
|
Loading…
Reference in a new issue