Commit 3f490f7f authored by Linus Torvalds's avatar Linus Torvalds

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

Pull f2fs updates from Jaegeuk Kim:
 "This patch-set includes the following major enhancement patches:
   - remount_fs callback function
   - restore parent inode number to enhance the fsync performance
   - xattr security labels
   - reduce the number of redundant lock/unlock data pages
   - avoid frequent write_inode calls

  The other minor bug fixes are as follows.
   - endian conversion bugs
   - various bugs in the roll-forward recovery routine"

* tag 'for-f2fs-3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (56 commits)
  f2fs: fix to recover i_size from roll-forward
  f2fs: remove the unused argument "sbi" of func destroy_fsync_dnodes()
  f2fs: remove reusing any prefree segments
  f2fs: code cleanup and simplify in func {find/add}_gc_inode
  f2fs: optimize the init_dirty_segmap function
  f2fs: fix an endian conversion bug detected by sparse
  f2fs: fix crc endian conversion
  f2fs: add remount_fs callback support
  f2fs: recover wrong pino after checkpoint during fsync
  f2fs: optimize do_write_data_page()
  f2fs: make locate_dirty_segment() as static
  f2fs: remove unnecessary parameter "offset" from __add_sum_entry()
  f2fs: avoid freqeunt write_inode calls
  f2fs: optimise the truncate_data_blocks_range() range
  f2fs: use the F2FS specific flags in f2fs_ioctl()
  f2fs: sync dir->i_size with its block allocation
  f2fs: fix i_blocks translation on various types of files
  f2fs: set sb->s_fs_info before calling parse_options()
  f2fs: support xattr security labels
  f2fs: fix iget/iput of dir during recovery
  ...
parents c4eb1b07 a1dd3c13
...@@ -98,8 +98,13 @@ Cleaning Overhead ...@@ -98,8 +98,13 @@ Cleaning Overhead
MOUNT OPTIONS MOUNT OPTIONS
================================================================================ ================================================================================
background_gc_off Turn off cleaning operations, namely garbage collection, background_gc=%s Turn on/off cleaning operations, namely garbage
triggered in background when I/O subsystem is idle. collection, triggered in background when I/O subsystem is
idle. If background_gc=on, it will turn on the garbage
collection and if background_gc=off, garbage collection
will be truned off.
Default value for this option is on. So garbage
collection is on by default.
disable_roll_forward Disable the roll-forward recovery routine disable_roll_forward Disable the roll-forward recovery routine
discard Issue discard/TRIM commands when a segment is cleaned. discard Issue discard/TRIM commands when a segment is cleaned.
no_heap Disable heap-style segment allocation which finds free no_heap Disable heap-style segment allocation which finds free
......
...@@ -51,3 +51,15 @@ config F2FS_FS_POSIX_ACL ...@@ -51,3 +51,15 @@ config F2FS_FS_POSIX_ACL
Linux website <http://acl.bestbits.at/>. Linux website <http://acl.bestbits.at/>.
If you don't know what Access Control Lists are, say N If you don't know what Access Control Lists are, say N
config F2FS_FS_SECURITY
bool "F2FS Security Labels"
depends on F2FS_FS_XATTR
help
Security labels provide an access control facility to support Linux
Security Models (LSMs) accepted by AppArmor, SELinux, Smack and TOMOYO
Linux. This option enables an extended attribute handler for file
security labels in the f2fs filesystem, so that it requires enabling
the extended attribute support in advance.
If you are not using a security module, say N.
...@@ -250,7 +250,7 @@ static int f2fs_set_acl(struct inode *inode, int type, struct posix_acl *acl) ...@@ -250,7 +250,7 @@ static int f2fs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
} }
} }
error = f2fs_setxattr(inode, name_index, "", value, size); error = f2fs_setxattr(inode, name_index, "", value, size, NULL);
kfree(value); kfree(value);
if (!error) if (!error)
......
...@@ -357,8 +357,8 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, ...@@ -357,8 +357,8 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
unsigned long blk_size = sbi->blocksize; unsigned long blk_size = sbi->blocksize;
struct f2fs_checkpoint *cp_block; struct f2fs_checkpoint *cp_block;
unsigned long long cur_version = 0, pre_version = 0; unsigned long long cur_version = 0, pre_version = 0;
unsigned int crc = 0;
size_t crc_offset; size_t crc_offset;
__u32 crc = 0;
/* Read the 1st cp block in this CP pack */ /* Read the 1st cp block in this CP pack */
cp_page_1 = get_meta_page(sbi, cp_addr); cp_page_1 = get_meta_page(sbi, cp_addr);
...@@ -369,7 +369,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, ...@@ -369,7 +369,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
if (crc_offset >= blk_size) if (crc_offset >= blk_size)
goto invalid_cp1; goto invalid_cp1;
crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset); crc = le32_to_cpu(*((__u32 *)((unsigned char *)cp_block + crc_offset)));
if (!f2fs_crc_valid(crc, cp_block, crc_offset)) if (!f2fs_crc_valid(crc, cp_block, crc_offset))
goto invalid_cp1; goto invalid_cp1;
...@@ -384,7 +384,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, ...@@ -384,7 +384,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
if (crc_offset >= blk_size) if (crc_offset >= blk_size)
goto invalid_cp2; goto invalid_cp2;
crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset); crc = le32_to_cpu(*((__u32 *)((unsigned char *)cp_block + crc_offset)));
if (!f2fs_crc_valid(crc, cp_block, crc_offset)) if (!f2fs_crc_valid(crc, cp_block, crc_offset))
goto invalid_cp2; goto invalid_cp2;
...@@ -450,13 +450,30 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi) ...@@ -450,13 +450,30 @@ int get_valid_checkpoint(struct f2fs_sb_info *sbi)
return -EINVAL; return -EINVAL;
} }
void set_dirty_dir_page(struct inode *inode, struct page *page) static int __add_dirty_inode(struct inode *inode, struct dir_inode_entry *new)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct list_head *head = &sbi->dir_inode_list; struct list_head *head = &sbi->dir_inode_list;
struct dir_inode_entry *new;
struct list_head *this; struct list_head *this;
list_for_each(this, head) {
struct dir_inode_entry *entry;
entry = list_entry(this, struct dir_inode_entry, list);
if (entry->inode == inode)
return -EEXIST;
}
list_add_tail(&new->list, head);
#ifdef CONFIG_F2FS_STAT_FS
sbi->n_dirty_dirs++;
#endif
return 0;
}
void set_dirty_dir_page(struct inode *inode, struct page *page)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct dir_inode_entry *new;
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode))
return; return;
retry: retry:
...@@ -469,23 +486,31 @@ void set_dirty_dir_page(struct inode *inode, struct page *page) ...@@ -469,23 +486,31 @@ void set_dirty_dir_page(struct inode *inode, struct page *page)
INIT_LIST_HEAD(&new->list); INIT_LIST_HEAD(&new->list);
spin_lock(&sbi->dir_inode_lock); spin_lock(&sbi->dir_inode_lock);
list_for_each(this, head) { if (__add_dirty_inode(inode, new))
struct dir_inode_entry *entry; kmem_cache_free(inode_entry_slab, new);
entry = list_entry(this, struct dir_inode_entry, list);
if (entry->inode == inode) {
kmem_cache_free(inode_entry_slab, new);
goto out;
}
}
list_add_tail(&new->list, head);
sbi->n_dirty_dirs++;
BUG_ON(!S_ISDIR(inode->i_mode));
out:
inc_page_count(sbi, F2FS_DIRTY_DENTS); inc_page_count(sbi, F2FS_DIRTY_DENTS);
inode_inc_dirty_dents(inode); inode_inc_dirty_dents(inode);
SetPagePrivate(page); SetPagePrivate(page);
spin_unlock(&sbi->dir_inode_lock);
}
void add_dirty_dir_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct dir_inode_entry *new;
retry:
new = kmem_cache_alloc(inode_entry_slab, GFP_NOFS);
if (!new) {
cond_resched();
goto retry;
}
new->inode = inode;
INIT_LIST_HEAD(&new->list);
spin_lock(&sbi->dir_inode_lock);
if (__add_dirty_inode(inode, new))
kmem_cache_free(inode_entry_slab, new);
spin_unlock(&sbi->dir_inode_lock); spin_unlock(&sbi->dir_inode_lock);
} }
...@@ -499,8 +524,10 @@ void remove_dirty_dir_inode(struct inode *inode) ...@@ -499,8 +524,10 @@ void remove_dirty_dir_inode(struct inode *inode)
return; return;
spin_lock(&sbi->dir_inode_lock); spin_lock(&sbi->dir_inode_lock);
if (atomic_read(&F2FS_I(inode)->dirty_dents)) if (atomic_read(&F2FS_I(inode)->dirty_dents)) {
goto out; spin_unlock(&sbi->dir_inode_lock);
return;
}
list_for_each(this, head) { list_for_each(this, head) {
struct dir_inode_entry *entry; struct dir_inode_entry *entry;
...@@ -508,12 +535,38 @@ void remove_dirty_dir_inode(struct inode *inode) ...@@ -508,12 +535,38 @@ void remove_dirty_dir_inode(struct inode *inode)
if (entry->inode == inode) { if (entry->inode == inode) {
list_del(&entry->list); list_del(&entry->list);
kmem_cache_free(inode_entry_slab, entry); kmem_cache_free(inode_entry_slab, entry);
#ifdef CONFIG_F2FS_STAT_FS
sbi->n_dirty_dirs--; sbi->n_dirty_dirs--;
#endif
break;
}
}
spin_unlock(&sbi->dir_inode_lock);
/* Only from the recovery routine */
if (is_inode_flag_set(F2FS_I(inode), FI_DELAY_IPUT)) {
clear_inode_flag(F2FS_I(inode), FI_DELAY_IPUT);
iput(inode);
}
}
struct inode *check_dirty_dir_inode(struct f2fs_sb_info *sbi, nid_t ino)
{
struct list_head *head = &sbi->dir_inode_list;
struct list_head *this;
struct inode *inode = NULL;
spin_lock(&sbi->dir_inode_lock);
list_for_each(this, head) {
struct dir_inode_entry *entry;
entry = list_entry(this, struct dir_inode_entry, list);
if (entry->inode->i_ino == ino) {
inode = entry->inode;
break; break;
} }
} }
out:
spin_unlock(&sbi->dir_inode_lock); spin_unlock(&sbi->dir_inode_lock);
return inode;
} }
void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi) void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi)
...@@ -595,7 +648,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) ...@@ -595,7 +648,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
block_t start_blk; block_t start_blk;
struct page *cp_page; struct page *cp_page;
unsigned int data_sum_blocks, orphan_blocks; unsigned int data_sum_blocks, orphan_blocks;
unsigned int crc32 = 0; __u32 crc32 = 0;
void *kaddr; void *kaddr;
int i; int i;
...@@ -664,8 +717,8 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) ...@@ -664,8 +717,8 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP)); get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP));
crc32 = f2fs_crc32(ckpt, le32_to_cpu(ckpt->checksum_offset)); crc32 = f2fs_crc32(ckpt, le32_to_cpu(ckpt->checksum_offset));
*(__le32 *)((unsigned char *)ckpt + *((__le32 *)((unsigned char *)ckpt +
le32_to_cpu(ckpt->checksum_offset)) le32_to_cpu(ckpt->checksum_offset)))
= cpu_to_le32(crc32); = cpu_to_le32(crc32);
start_blk = __start_cp_addr(sbi); start_blk = __start_cp_addr(sbi);
......
...@@ -68,7 +68,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs, ...@@ -68,7 +68,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs,
struct buffer_head *bh_result) struct buffer_head *bh_result)
{ {
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
#ifdef CONFIG_F2FS_STAT_FS
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
#endif
pgoff_t start_fofs, end_fofs; pgoff_t start_fofs, end_fofs;
block_t start_blkaddr; block_t start_blkaddr;
...@@ -78,7 +80,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs, ...@@ -78,7 +80,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs,
return 0; return 0;
} }
#ifdef CONFIG_F2FS_STAT_FS
sbi->total_hit_ext++; sbi->total_hit_ext++;
#endif
start_fofs = fi->ext.fofs; start_fofs = fi->ext.fofs;
end_fofs = fi->ext.fofs + fi->ext.len - 1; end_fofs = fi->ext.fofs + fi->ext.len - 1;
start_blkaddr = fi->ext.blk_addr; start_blkaddr = fi->ext.blk_addr;
...@@ -96,7 +100,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs, ...@@ -96,7 +100,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs,
else else
bh_result->b_size = UINT_MAX; bh_result->b_size = UINT_MAX;
#ifdef CONFIG_F2FS_STAT_FS
sbi->read_hit_ext++; sbi->read_hit_ext++;
#endif
read_unlock(&fi->ext.ext_lock); read_unlock(&fi->ext.ext_lock);
return 1; return 1;
} }
...@@ -199,7 +205,7 @@ struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync) ...@@ -199,7 +205,7 @@ struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
if (dn.data_blkaddr == NEW_ADDR) if (dn.data_blkaddr == NEW_ADDR)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
page = grab_cache_page(mapping, index); page = grab_cache_page_write_begin(mapping, index, AOP_FLAG_NOFS);
if (!page) if (!page)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -233,18 +239,23 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index) ...@@ -233,18 +239,23 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
struct page *page; struct page *page;
int err; int err;
repeat:
page = grab_cache_page_write_begin(mapping, index, AOP_FLAG_NOFS);
if (!page)
return ERR_PTR(-ENOMEM);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, index, LOOKUP_NODE); err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
if (err) if (err) {
f2fs_put_page(page, 1);
return ERR_PTR(err); return ERR_PTR(err);
}
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
if (dn.data_blkaddr == NULL_ADDR) if (dn.data_blkaddr == NULL_ADDR) {
f2fs_put_page(page, 1);
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
repeat: }
page = grab_cache_page(mapping, index);
if (!page)
return ERR_PTR(-ENOMEM);
if (PageUptodate(page)) if (PageUptodate(page))
return page; return page;
...@@ -274,9 +285,10 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index) ...@@ -274,9 +285,10 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
* *
* Also, caller should grab and release a mutex by calling mutex_lock_op() and * Also, caller should grab and release a mutex by calling mutex_lock_op() and
* mutex_unlock_op(). * mutex_unlock_op().
* Note that, npage is set only by make_empty_dir.
*/ */
struct page *get_new_data_page(struct inode *inode, pgoff_t index, struct page *get_new_data_page(struct inode *inode,
bool new_i_size) struct page *npage, pgoff_t index, bool new_i_size)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
...@@ -284,18 +296,20 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index, ...@@ -284,18 +296,20 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
struct dnode_of_data dn; struct dnode_of_data dn;
int err; int err;
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, npage, npage, 0);
err = get_dnode_of_data(&dn, index, ALLOC_NODE); err = get_dnode_of_data(&dn, index, ALLOC_NODE);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
if (dn.data_blkaddr == NULL_ADDR) { if (dn.data_blkaddr == NULL_ADDR) {
if (reserve_new_block(&dn)) { if (reserve_new_block(&dn)) {
f2fs_put_dnode(&dn); if (!npage)
f2fs_put_dnode(&dn);
return ERR_PTR(-ENOSPC); return ERR_PTR(-ENOSPC);
} }
} }
f2fs_put_dnode(&dn); if (!npage)
f2fs_put_dnode(&dn);
repeat: repeat:
page = grab_cache_page(mapping, index); page = grab_cache_page(mapping, index);
if (!page) if (!page)
...@@ -325,6 +339,8 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index, ...@@ -325,6 +339,8 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
if (new_i_size && if (new_i_size &&
i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT)); i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT));
/* Only the directory inode sets new_i_size */
set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR);
mark_inode_dirty_sync(inode); mark_inode_dirty_sync(inode);
} }
return page; return page;
...@@ -481,8 +497,9 @@ int do_write_data_page(struct page *page) ...@@ -481,8 +497,9 @@ int do_write_data_page(struct page *page)
* If current allocation needs SSR, * If current allocation needs SSR,
* it had better in-place writes for updated data. * it had better in-place writes for updated data.
*/ */
if (old_blk_addr != NEW_ADDR && !is_cold_data(page) && if (unlikely(old_blk_addr != NEW_ADDR &&
need_inplace_update(inode)) { !is_cold_data(page) &&
need_inplace_update(inode))) {
rewrite_data_page(F2FS_SB(inode->i_sb), page, rewrite_data_page(F2FS_SB(inode->i_sb), page,
old_blk_addr); old_blk_addr);
} else { } else {
...@@ -684,6 +701,27 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, ...@@ -684,6 +701,27 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
return err; return err;
} }
static int f2fs_write_end(struct file *file,
struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
struct page *page, void *fsdata)
{
struct inode *inode = page->mapping->host;
SetPageUptodate(page);
set_page_dirty(page);
if (pos + copied > i_size_read(inode)) {
i_size_write(inode, pos + copied);
mark_inode_dirty(inode);
update_inode_page(inode);
}
unlock_page(page);
page_cache_release(page);
return copied;
}
static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset, unsigned long nr_segs) const struct iovec *iov, loff_t offset, unsigned long nr_segs)
{ {
...@@ -741,7 +779,7 @@ const struct address_space_operations f2fs_dblock_aops = { ...@@ -741,7 +779,7 @@ const struct address_space_operations f2fs_dblock_aops = {
.writepage = f2fs_write_data_page, .writepage = f2fs_write_data_page,
.writepages = f2fs_write_data_pages, .writepages = f2fs_write_data_pages,
.write_begin = f2fs_write_begin, .write_begin = f2fs_write_begin,
.write_end = nobh_write_end, .write_end = f2fs_write_end,
.set_page_dirty = f2fs_set_data_page_dirty, .set_page_dirty = f2fs_set_data_page_dirty,
.invalidatepage = f2fs_invalidate_data_page, .invalidatepage = f2fs_invalidate_data_page,
.releasepage = f2fs_release_data_page, .releasepage = f2fs_release_data_page,
......
...@@ -175,12 +175,12 @@ static void update_mem_info(struct f2fs_sb_info *sbi) ...@@ -175,12 +175,12 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
static int stat_show(struct seq_file *s, void *v) static int stat_show(struct seq_file *s, void *v)
{ {
struct f2fs_stat_info *si, *next; struct f2fs_stat_info *si;
int i = 0; int i = 0;
int j; int j;
mutex_lock(&f2fs_stat_mutex); mutex_lock(&f2fs_stat_mutex);
list_for_each_entry_safe(si, next, &f2fs_stat_list, stat_list) { list_for_each_entry(si, &f2fs_stat_list, stat_list) {
char devname[BDEVNAME_SIZE]; char devname[BDEVNAME_SIZE];
update_general_status(si->sbi); update_general_status(si->sbi);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
#include "acl.h" #include "acl.h"
#include "xattr.h"
static unsigned long dir_blocks(struct inode *inode) static unsigned long dir_blocks(struct inode *inode)
{ {
...@@ -215,9 +216,9 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, ...@@ -215,9 +216,9 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p) struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p)
{ {
struct page *page = NULL; struct page *page;
struct f2fs_dir_entry *de = NULL; struct f2fs_dir_entry *de;
struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_block *dentry_blk;
page = get_lock_data_page(dir, 0); page = get_lock_data_page(dir, 0);
if (IS_ERR(page)) if (IS_ERR(page))
...@@ -264,15 +265,10 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, ...@@ -264,15 +265,10 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
} }
void init_dent_inode(const struct qstr *name, struct page *ipage) static void init_dent_inode(const struct qstr *name, struct page *ipage)
{ {
struct f2fs_node *rn; struct f2fs_node *rn;
if (IS_ERR(ipage))
return;
wait_on_page_writeback(ipage);
/* copy name info. to this inode page */ /* copy name info. to this inode page */
rn = (struct f2fs_node *)page_address(ipage); rn = (struct f2fs_node *)page_address(ipage);
rn->i.i_namelen = cpu_to_le32(name->len); rn->i.i_namelen = cpu_to_le32(name->len);
...@@ -280,14 +276,15 @@ void init_dent_inode(const struct qstr *name, struct page *ipage) ...@@ -280,14 +276,15 @@ void init_dent_inode(const struct qstr *name, struct page *ipage)
set_page_dirty(ipage); set_page_dirty(ipage);
} }
static int make_empty_dir(struct inode *inode, struct inode *parent) static int make_empty_dir(struct inode *inode,
struct inode *parent, struct page *page)
{ {
struct page *dentry_page; struct page *dentry_page;
struct f2fs_dentry_block *dentry_blk; struct f2fs_dentry_block *dentry_blk;
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
void *kaddr; void *kaddr;
dentry_page = get_new_data_page(inode, 0, true); dentry_page = get_new_data_page(inode, page, 0, true);
if (IS_ERR(dentry_page)) if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page); return PTR_ERR(dentry_page);
...@@ -317,63 +314,76 @@ static int make_empty_dir(struct inode *inode, struct inode *parent) ...@@ -317,63 +314,76 @@ static int make_empty_dir(struct inode *inode, struct inode *parent)
return 0; return 0;
} }
static int init_inode_metadata(struct inode *inode, static struct page *init_inode_metadata(struct inode *inode,
struct inode *dir, const struct qstr *name) struct inode *dir, const struct qstr *name)
{ {
struct page *page;
int err;
if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
int err; page = new_inode_page(inode, name);
err = new_inode_page(inode, name); if (IS_ERR(page))
if (err) return page;
return err;
if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
err = make_empty_dir(inode, dir); err = make_empty_dir(inode, dir, page);
if (err) { if (err)
remove_inode_page(inode); goto error;
return err;
}
} }
err = f2fs_init_acl(inode, dir); err = f2fs_init_acl(inode, dir);
if (err) { if (err)
remove_inode_page(inode); goto error;
return err;
} err = f2fs_init_security(inode, dir, name, page);
if (err)
goto error;
wait_on_page_writeback(page);
} else { } else {
struct page *ipage; page = get_node_page(F2FS_SB(dir->i_sb), inode->i_ino);
ipage = get_node_page(F2FS_SB(dir->i_sb), inode->i_ino); if (IS_ERR(page))
if (IS_ERR(ipage)) return page;
return PTR_ERR(ipage);
set_cold_node(inode, ipage); wait_on_page_writeback(page);
init_dent_inode(name, ipage); set_cold_node(inode, page);
f2fs_put_page(ipage, 1);
} }
init_dent_inode(name, page);
/*
* This file should be checkpointed during fsync.
* We lost i_pino from now on.
*/
if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) {
file_lost_pino(inode);
inc_nlink(inode); inc_nlink(inode);
update_inode_page(inode);
} }
return 0; return page;
error:
f2fs_put_page(page, 1);
remove_inode_page(inode);
return ERR_PTR(err);
} }
static void update_parent_metadata(struct inode *dir, struct inode *inode, static void update_parent_metadata(struct inode *dir, struct inode *inode,
unsigned int current_depth) unsigned int current_depth)
{ {
bool need_dir_update = false;
if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
inc_nlink(dir); inc_nlink(dir);
need_dir_update = true; set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
} }
clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); clear_inode_flag(F2FS_I(inode), FI_NEW_INODE);
} }
dir->i_mtime = dir->i_ctime = CURRENT_TIME; dir->i_mtime = dir->i_ctime = CURRENT_TIME;
if (F2FS_I(dir)->i_current_depth != current_depth) { if (F2FS_I(dir)->i_current_depth != current_depth) {
F2FS_I(dir)->i_current_depth = current_depth; F2FS_I(dir)->i_current_depth = current_depth;
need_dir_update = true; set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
} }
if (need_dir_update) if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR))
update_inode_page(dir); update_inode_page(dir);
else else
mark_inode_dirty(dir); mark_inode_dirty(dir);
...@@ -423,6 +433,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in ...@@ -423,6 +433,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in
struct page *dentry_page = NULL; struct page *dentry_page = NULL;
struct f2fs_dentry_block *dentry_blk = NULL; struct f2fs_dentry_block *dentry_blk = NULL;
int slots = GET_DENTRY_SLOTS(namelen); int slots = GET_DENTRY_SLOTS(namelen);
struct page *page;
int err = 0; int err = 0;
int i; int i;
...@@ -448,7 +459,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in ...@@ -448,7 +459,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in
bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket)); bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket));
for (block = bidx; block <= (bidx + nblock - 1); block++) { for (block = bidx; block <= (bidx + nblock - 1); block++) {
dentry_page = get_new_data_page(dir, block, true); dentry_page = get_new_data_page(dir, NULL, block, true);
if (IS_ERR(dentry_page)) if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page); return PTR_ERR(dentry_page);
...@@ -465,12 +476,13 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in ...@@ -465,12 +476,13 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in
++level; ++level;
goto start; goto start;
add_dentry: add_dentry:
err = init_inode_metadata(inode, dir, name);
if (err)
goto fail;
wait_on_page_writeback(dentry_page); wait_on_page_writeback(dentry_page);
page = init_inode_metadata(inode, dir, name);
if (IS_ERR(page)) {
err = PTR_ERR(page);
goto fail;
}
de = &dentry_blk->dentry[bit_pos]; de = &dentry_blk->dentry[bit_pos];
de->hash_code = dentry_hash; de->hash_code = dentry_hash;
de->name_len = cpu_to_le16(namelen); de->name_len = cpu_to_le16(namelen);
...@@ -481,11 +493,14 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in ...@@ -481,11 +493,14 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in
test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
set_page_dirty(dentry_page); set_page_dirty(dentry_page);
update_parent_metadata(dir, inode, current_depth); /* we don't need to mark_inode_dirty now */
/* update parent inode number before releasing dentry page */
F2FS_I(inode)->i_pino = dir->i_ino; 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: fail:
clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR);
kunmap(dentry_page); kunmap(dentry_page);
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);
return err; return err;
......
...@@ -37,21 +37,35 @@ ...@@ -37,21 +37,35 @@
typecheck(unsigned long long, b) && \ typecheck(unsigned long long, b) && \
((long long)((a) - (b)) > 0)) ((long long)((a) - (b)) > 0))
typedef u64 block_t; typedef u32 block_t; /*
* should not change u32, since it is the on-disk block
* address format, __le32.
*/
typedef u32 nid_t; typedef u32 nid_t;
struct f2fs_mount_info { struct f2fs_mount_info {
unsigned int opt; unsigned int opt;
}; };
static inline __u32 f2fs_crc32(void *buff, size_t len) #define CRCPOLY_LE 0xedb88320
static inline __u32 f2fs_crc32(void *buf, size_t len)
{ {
return crc32_le(F2FS_SUPER_MAGIC, buff, len); unsigned char *p = (unsigned char *)buf;
__u32 crc = F2FS_SUPER_MAGIC;
int i;
while (len--) {
crc ^= *p++;
for (i = 0; i < 8; i++)
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
}
return crc;
} }
static inline bool f2fs_crc_valid(__u32 blk_crc, void *buff, size_t buff_size) static inline bool f2fs_crc_valid(__u32 blk_crc, void *buf, size_t buf_size)
{ {
return f2fs_crc32(buff, buff_size) == blk_crc; return f2fs_crc32(buf, buf_size) == blk_crc;
} }
/* /*
...@@ -148,7 +162,7 @@ struct extent_info { ...@@ -148,7 +162,7 @@ struct extent_info {
* i_advise uses FADVISE_XXX_BIT. We can add additional hints later. * i_advise uses FADVISE_XXX_BIT. We can add additional hints later.
*/ */
#define FADVISE_COLD_BIT 0x01 #define FADVISE_COLD_BIT 0x01
#define FADVISE_CP_BIT 0x02 #define FADVISE_LOST_PINO_BIT 0x02
struct f2fs_inode_info { struct f2fs_inode_info {
struct inode vfs_inode; /* serve a vfs inode */ struct inode vfs_inode; /* serve a vfs inode */
...@@ -369,7 +383,6 @@ struct f2fs_sb_info { ...@@ -369,7 +383,6 @@ struct f2fs_sb_info {
/* for directory inode management */ /* for directory inode management */
struct list_head dir_inode_list; /* dir inode list */ struct list_head dir_inode_list; /* dir inode list */
spinlock_t dir_inode_lock; /* for dir inode list lock */ spinlock_t dir_inode_lock; /* for dir inode list lock */
unsigned int n_dirty_dirs; /* # of dir inodes */
/* basic file system units */ /* basic file system units */
unsigned int log_sectors_per_block; /* log2 sectors per block */ unsigned int log_sectors_per_block; /* log2 sectors per block */
...@@ -406,12 +419,15 @@ struct f2fs_sb_info { ...@@ -406,12 +419,15 @@ struct f2fs_sb_info {
* for stat information. * for stat information.
* one is for the LFS mode, and the other is for the SSR mode. * one is for the LFS mode, and the other is for the SSR mode.
*/ */
#ifdef CONFIG_F2FS_STAT_FS
struct f2fs_stat_info *stat_info; /* FS status information */ struct f2fs_stat_info *stat_info; /* FS status information */
unsigned int segment_count[2]; /* # of allocated segments */ unsigned int segment_count[2]; /* # of allocated segments */
unsigned int block_count[2]; /* # of allocated blocks */ unsigned int block_count[2]; /* # of allocated blocks */
unsigned int last_victim[2]; /* last victim segment # */
int total_hit_ext, read_hit_ext; /* extent cache hit ratio */ int total_hit_ext, read_hit_ext; /* extent cache hit ratio */
int bg_gc; /* background gc calls */ int bg_gc; /* background gc calls */
unsigned int n_dirty_dirs; /* # of dir inodes */
#endif
unsigned int last_victim[2]; /* last victim segment # */
spinlock_t stat_lock; /* lock for stat operations */ spinlock_t stat_lock; /* lock for stat operations */
}; };
...@@ -495,9 +511,17 @@ static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) ...@@ -495,9 +511,17 @@ static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f)
static inline void mutex_lock_all(struct f2fs_sb_info *sbi) static inline void mutex_lock_all(struct f2fs_sb_info *sbi)
{ {
int i = 0; int i;
for (; i < NR_GLOBAL_LOCKS; i++)
mutex_lock(&sbi->fs_lock[i]); for (i = 0; i < NR_GLOBAL_LOCKS; i++) {
/*
* This is the only time we take multiple fs_lock[]
* instances; the order is immaterial since we
* always hold cp_mutex, which serializes multiple
* such operations.
*/
mutex_lock_nest_lock(&sbi->fs_lock[i], &sbi->cp_mutex);
}
} }
static inline void mutex_unlock_all(struct f2fs_sb_info *sbi) static inline void mutex_unlock_all(struct f2fs_sb_info *sbi)
...@@ -843,9 +867,12 @@ static inline int f2fs_clear_bit(unsigned int nr, char *addr) ...@@ -843,9 +867,12 @@ static inline int f2fs_clear_bit(unsigned int nr, char *addr)
/* used for f2fs_inode_info->flags */ /* used for f2fs_inode_info->flags */
enum { enum {
FI_NEW_INODE, /* indicate newly allocated inode */ FI_NEW_INODE, /* indicate newly allocated inode */
FI_DIRTY_INODE, /* indicate inode is dirty or not */
FI_INC_LINK, /* need to increment i_nlink */ FI_INC_LINK, /* need to increment i_nlink */
FI_ACL_MODE, /* indicate acl mode */ FI_ACL_MODE, /* indicate acl mode */
FI_NO_ALLOC, /* should not allocate any blocks */ FI_NO_ALLOC, /* should not allocate any blocks */
FI_UPDATE_DIR, /* should update inode block for consistency */
FI_DELAY_IPUT, /* used for the recovery */
}; };
static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
...@@ -878,14 +905,21 @@ static inline int cond_clear_inode_flag(struct f2fs_inode_info *fi, int flag) ...@@ -878,14 +905,21 @@ static inline int cond_clear_inode_flag(struct f2fs_inode_info *fi, int flag)
return 0; return 0;
} }
static inline int f2fs_readonly(struct super_block *sb)
{
return sb->s_flags & MS_RDONLY;
}
/* /*
* file.c * file.c
*/ */
int f2fs_sync_file(struct file *, loff_t, loff_t, int); int f2fs_sync_file(struct file *, loff_t, loff_t, int);
void truncate_data_blocks(struct dnode_of_data *); void truncate_data_blocks(struct dnode_of_data *);
void f2fs_truncate(struct inode *); void f2fs_truncate(struct inode *);
int f2fs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
int f2fs_setattr(struct dentry *, struct iattr *); int f2fs_setattr(struct dentry *, struct iattr *);
int truncate_hole(struct inode *, pgoff_t, pgoff_t); int truncate_hole(struct inode *, pgoff_t, pgoff_t);
int truncate_data_blocks_range(struct dnode_of_data *, int);
long f2fs_ioctl(struct file *, unsigned int, unsigned long); long f2fs_ioctl(struct file *, unsigned int, unsigned long);
long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long); long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long);
...@@ -913,7 +947,6 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); ...@@ -913,7 +947,6 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **);
ino_t f2fs_inode_by_name(struct inode *, struct qstr *); ino_t f2fs_inode_by_name(struct inode *, struct qstr *);
void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
struct page *, struct inode *); struct page *, struct inode *);
void init_dent_inode(const struct qstr *, struct page *);
int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *); int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *);
void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *);
int f2fs_make_empty(struct inode *, struct inode *); int f2fs_make_empty(struct inode *, struct inode *);
...@@ -948,8 +981,8 @@ void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); ...@@ -948,8 +981,8 @@ void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
int truncate_inode_blocks(struct inode *, pgoff_t); int truncate_inode_blocks(struct inode *, pgoff_t);
int remove_inode_page(struct inode *); int remove_inode_page(struct inode *);
int new_inode_page(struct inode *, const struct qstr *); struct page *new_inode_page(struct inode *, const struct qstr *);
struct page *new_node_page(struct dnode_of_data *, unsigned int); struct page *new_node_page(struct dnode_of_data *, unsigned int, struct page *);
void ra_node_page(struct f2fs_sb_info *, nid_t); void ra_node_page(struct f2fs_sb_info *, nid_t);
struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); struct page *get_node_page(struct f2fs_sb_info *, pgoff_t);
struct page *get_node_page_ra(struct page *, int); struct page *get_node_page_ra(struct page *, int);
...@@ -974,7 +1007,6 @@ void destroy_node_manager_caches(void); ...@@ -974,7 +1007,6 @@ void destroy_node_manager_caches(void);
*/ */
void f2fs_balance_fs(struct f2fs_sb_info *); void f2fs_balance_fs(struct f2fs_sb_info *);
void invalidate_blocks(struct f2fs_sb_info *, block_t); void invalidate_blocks(struct f2fs_sb_info *, block_t);
void locate_dirty_segment(struct f2fs_sb_info *, unsigned int);
void clear_prefree_segments(struct f2fs_sb_info *); void clear_prefree_segments(struct f2fs_sb_info *);
int npages_for_summary_flush(struct f2fs_sb_info *); int npages_for_summary_flush(struct f2fs_sb_info *);
void allocate_new_segments(struct f2fs_sb_info *); void allocate_new_segments(struct f2fs_sb_info *);
...@@ -1011,7 +1043,9 @@ void remove_orphan_inode(struct f2fs_sb_info *, nid_t); ...@@ -1011,7 +1043,9 @@ void remove_orphan_inode(struct f2fs_sb_info *, nid_t);
int recover_orphan_inodes(struct f2fs_sb_info *); int recover_orphan_inodes(struct f2fs_sb_info *);
int get_valid_checkpoint(struct f2fs_sb_info *); int get_valid_checkpoint(struct f2fs_sb_info *);
void set_dirty_dir_page(struct inode *, struct page *); void set_dirty_dir_page(struct inode *, struct page *);
void add_dirty_dir_inode(struct inode *);
void remove_dirty_dir_inode(struct inode *); void remove_dirty_dir_inode(struct inode *);
struct inode *check_dirty_dir_inode(struct f2fs_sb_info *, nid_t);
void sync_dirty_dir_inodes(struct f2fs_sb_info *); void sync_dirty_dir_inodes(struct f2fs_sb_info *);
void write_checkpoint(struct f2fs_sb_info *, bool); void write_checkpoint(struct f2fs_sb_info *, bool);
void init_orphan_info(struct f2fs_sb_info *); void init_orphan_info(struct f2fs_sb_info *);
...@@ -1025,7 +1059,7 @@ int reserve_new_block(struct dnode_of_data *); ...@@ -1025,7 +1059,7 @@ int reserve_new_block(struct dnode_of_data *);
void update_extent_cache(block_t, struct dnode_of_data *); void update_extent_cache(block_t, struct dnode_of_data *);
struct page *find_data_page(struct inode *, pgoff_t, bool); struct page *find_data_page(struct inode *, pgoff_t, bool);
struct page *get_lock_data_page(struct inode *, pgoff_t); struct page *get_lock_data_page(struct inode *, pgoff_t);
struct page *get_new_data_page(struct inode *, pgoff_t, bool); struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool);
int f2fs_readpage(struct f2fs_sb_info *, struct page *, block_t, int); int f2fs_readpage(struct f2fs_sb_info *, struct page *, block_t, int);
int do_write_data_page(struct page *); int do_write_data_page(struct page *);
......
...@@ -63,9 +63,10 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, ...@@ -63,9 +63,10 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, ilock); mutex_unlock_op(sbi, ilock);
file_update_time(vma->vm_file);
lock_page(page); lock_page(page);
if (page->mapping != inode->i_mapping || if (page->mapping != inode->i_mapping ||
page_offset(page) >= i_size_read(inode) || page_offset(page) > i_size_read(inode) ||
!PageUptodate(page)) { !PageUptodate(page)) {
unlock_page(page); unlock_page(page);
err = -EFAULT; err = -EFAULT;
...@@ -76,10 +77,7 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, ...@@ -76,10 +77,7 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
* check to see if the page is mapped already (no holes) * check to see if the page is mapped already (no holes)
*/ */
if (PageMappedToDisk(page)) if (PageMappedToDisk(page))
goto out; goto mapped;
/* fill the page */
wait_on_page_writeback(page);
/* page is wholly or partially inside EOF */ /* page is wholly or partially inside EOF */
if (((page->index + 1) << PAGE_CACHE_SHIFT) > i_size_read(inode)) { if (((page->index + 1) << PAGE_CACHE_SHIFT) > i_size_read(inode)) {
...@@ -90,7 +88,9 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, ...@@ -90,7 +88,9 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
set_page_dirty(page); set_page_dirty(page);
SetPageUptodate(page); SetPageUptodate(page);
file_update_time(vma->vm_file); mapped:
/* fill the page */
wait_on_page_writeback(page);
out: out:
sb_end_pagefault(inode->i_sb); sb_end_pagefault(inode->i_sb);
return block_page_mkwrite_return(err); return block_page_mkwrite_return(err);
...@@ -102,6 +102,24 @@ static const struct vm_operations_struct f2fs_file_vm_ops = { ...@@ -102,6 +102,24 @@ static const struct vm_operations_struct f2fs_file_vm_ops = {
.remap_pages = generic_file_remap_pages, .remap_pages = generic_file_remap_pages,
}; };
static int get_parent_ino(struct inode *inode, nid_t *pino)
{
struct dentry *dentry;
inode = igrab(inode);
dentry = d_find_any_alias(inode);
iput(inode);
if (!dentry)
return 0;
inode = igrab(dentry->d_parent->d_inode);
dput(dentry);
*pino = inode->i_ino;
iput(inode);
return 1;
}
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{ {
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
...@@ -114,7 +132,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -114,7 +132,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
.for_reclaim = 0, .for_reclaim = 0,
}; };
if (inode->i_sb->s_flags & MS_RDONLY) if (f2fs_readonly(inode->i_sb))
return 0; return 0;
trace_f2fs_sync_file_enter(inode); trace_f2fs_sync_file_enter(inode);
...@@ -134,7 +152,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -134,7 +152,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
need_cp = true; need_cp = true;
else if (is_cp_file(inode)) else if (file_wrong_pino(inode))
need_cp = true; need_cp = true;
else if (!space_for_roll_forward(sbi)) else if (!space_for_roll_forward(sbi))
need_cp = true; need_cp = true;
...@@ -142,11 +160,23 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -142,11 +160,23 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
need_cp = true; need_cp = true;
if (need_cp) { if (need_cp) {
nid_t pino;
/* all the dirty node pages should be flushed for POR */ /* all the dirty node pages should be flushed for POR */
ret = f2fs_sync_fs(inode->i_sb, 1); ret = f2fs_sync_fs(inode->i_sb, 1);
if (file_wrong_pino(inode) && inode->i_nlink == 1 &&
get_parent_ino(inode, &pino)) {
F2FS_I(inode)->i_pino = pino;
file_got_pino(inode);
mark_inode_dirty_sync(inode);
ret = f2fs_write_inode(inode, NULL);
if (ret)
goto out;
}
} else { } else {
/* if there is no written node page, write its inode page */ /* if there is no written node page, write its inode page */
while (!sync_node_pages(sbi, inode->i_ino, &wbc)) { while (!sync_node_pages(sbi, inode->i_ino, &wbc)) {
mark_inode_dirty_sync(inode);
ret = f2fs_write_inode(inode, NULL); ret = f2fs_write_inode(inode, NULL);
if (ret) if (ret)
goto out; goto out;
...@@ -168,7 +198,7 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -168,7 +198,7 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
return 0; return 0;
} }
static int truncate_data_blocks_range(struct dnode_of_data *dn, int count) int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
{ {
int nr_free = 0, ofs = dn->ofs_in_node; int nr_free = 0, ofs = dn->ofs_in_node;
struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb);
...@@ -185,10 +215,10 @@ static int truncate_data_blocks_range(struct dnode_of_data *dn, int count) ...@@ -185,10 +215,10 @@ static int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
update_extent_cache(NULL_ADDR, dn); update_extent_cache(NULL_ADDR, dn);
invalidate_blocks(sbi, blkaddr); invalidate_blocks(sbi, blkaddr);
dec_valid_block_count(sbi, dn->inode, 1);
nr_free++; nr_free++;
} }
if (nr_free) { if (nr_free) {
dec_valid_block_count(sbi, dn->inode, nr_free);
set_page_dirty(dn->node_page); set_page_dirty(dn->node_page);
sync_inode_page(dn); sync_inode_page(dn);
} }
...@@ -291,7 +321,7 @@ void f2fs_truncate(struct inode *inode) ...@@ -291,7 +321,7 @@ void f2fs_truncate(struct inode *inode)
} }
} }
static int f2fs_getattr(struct vfsmount *mnt, int f2fs_getattr(struct vfsmount *mnt,
struct dentry *dentry, struct kstat *stat) struct dentry *dentry, struct kstat *stat)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -387,7 +417,7 @@ static void fill_zero(struct inode *inode, pgoff_t index, ...@@ -387,7 +417,7 @@ static void fill_zero(struct inode *inode, pgoff_t index,
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
ilock = mutex_lock_op(sbi); ilock = mutex_lock_op(sbi);
page = get_new_data_page(inode, index, false); page = get_new_data_page(inode, NULL, index, false);
mutex_unlock_op(sbi, ilock); mutex_unlock_op(sbi, ilock);
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
...@@ -575,10 +605,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -575,10 +605,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
int ret; int ret;
switch (cmd) { switch (cmd) {
case FS_IOC_GETFLAGS: case F2FS_IOC_GETFLAGS:
flags = fi->i_flags & FS_FL_USER_VISIBLE; flags = fi->i_flags & FS_FL_USER_VISIBLE;
return put_user(flags, (int __user *) arg); return put_user(flags, (int __user *) arg);
case FS_IOC_SETFLAGS: case F2FS_IOC_SETFLAGS:
{ {
unsigned int oldflags; unsigned int oldflags;
......
...@@ -76,7 +76,9 @@ static int gc_thread_func(void *data) ...@@ -76,7 +76,9 @@ static int gc_thread_func(void *data)
else else
wait_ms = increase_sleep_time(wait_ms); wait_ms = increase_sleep_time(wait_ms);
#ifdef CONFIG_F2FS_STAT_FS
sbi->bg_gc++; sbi->bg_gc++;
#endif
/* if return value is not zero, no victim was selected */ /* if return value is not zero, no victim was selected */
if (f2fs_gc(sbi)) if (f2fs_gc(sbi))
...@@ -89,23 +91,28 @@ int start_gc_thread(struct f2fs_sb_info *sbi) ...@@ -89,23 +91,28 @@ int start_gc_thread(struct f2fs_sb_info *sbi)
{ {
struct f2fs_gc_kthread *gc_th; struct f2fs_gc_kthread *gc_th;
dev_t dev = sbi->sb->s_bdev->bd_dev; dev_t dev = sbi->sb->s_bdev->bd_dev;
int err = 0;
if (!test_opt(sbi, BG_GC)) if (!test_opt(sbi, BG_GC))
return 0; goto out;
gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL);
if (!gc_th) if (!gc_th) {
return -ENOMEM; err = -ENOMEM;
goto out;
}
sbi->gc_thread = gc_th; sbi->gc_thread = gc_th;
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
"f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev));
if (IS_ERR(gc_th->f2fs_gc_task)) { if (IS_ERR(gc_th->f2fs_gc_task)) {
err = PTR_ERR(gc_th->f2fs_gc_task);
kfree(gc_th); kfree(gc_th);
sbi->gc_thread = NULL; sbi->gc_thread = NULL;
return -ENOMEM;
} }
return 0;
out:
return err;
} }
void stop_gc_thread(struct f2fs_sb_info *sbi) void stop_gc_thread(struct f2fs_sb_info *sbi)
...@@ -234,14 +241,14 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, ...@@ -234,14 +241,14 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
struct victim_sel_policy p; struct victim_sel_policy p;
unsigned int secno; unsigned int secno, max_cost;
int nsearched = 0; int nsearched = 0;
p.alloc_mode = alloc_mode; p.alloc_mode = alloc_mode;
select_policy(sbi, gc_type, type, &p); select_policy(sbi, gc_type, type, &p);
p.min_segno = NULL_SEGNO; p.min_segno = NULL_SEGNO;
p.min_cost = get_max_cost(sbi, &p); p.min_cost = max_cost = get_max_cost(sbi, &p);
mutex_lock(&dirty_i->seglist_lock); mutex_lock(&dirty_i->seglist_lock);
...@@ -280,7 +287,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, ...@@ -280,7 +287,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
p.min_cost = cost; p.min_cost = cost;
} }
if (cost == get_max_cost(sbi, &p)) if (cost == max_cost)
continue; continue;
if (nsearched++ >= MAX_VICTIM_SEARCH) { if (nsearched++ >= MAX_VICTIM_SEARCH) {
...@@ -288,8 +295,8 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi, ...@@ -288,8 +295,8 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
break; break;
} }
} }
got_it:
if (p.min_segno != NULL_SEGNO) { if (p.min_segno != NULL_SEGNO) {
got_it:
if (p.alloc_mode == LFS) { if (p.alloc_mode == LFS) {
secno = GET_SECNO(sbi, p.min_segno); secno = GET_SECNO(sbi, p.min_segno);
if (gc_type == FG_GC) if (gc_type == FG_GC)
...@@ -314,28 +321,21 @@ static const struct victim_selection default_v_ops = { ...@@ -314,28 +321,21 @@ static const struct victim_selection default_v_ops = {
static struct inode *find_gc_inode(nid_t ino, struct list_head *ilist) static struct inode *find_gc_inode(nid_t ino, struct list_head *ilist)
{ {
struct list_head *this;
struct inode_entry *ie; struct inode_entry *ie;
list_for_each(this, ilist) { list_for_each_entry(ie, ilist, list)
ie = list_entry(this, struct inode_entry, list);
if (ie->inode->i_ino == ino) if (ie->inode->i_ino == ino)
return ie->inode; return ie->inode;
}
return NULL; return NULL;
} }
static void add_gc_inode(struct inode *inode, struct list_head *ilist) static void add_gc_inode(struct inode *inode, struct list_head *ilist)
{ {
struct list_head *this; struct inode_entry *new_ie;
struct inode_entry *new_ie, *ie;
list_for_each(this, ilist) { if (inode == find_gc_inode(inode->i_ino, ilist)) {
ie = list_entry(this, struct inode_entry, list); iput(inode);
if (ie->inode == inode) { return;
iput(inode);
return;
}
} }
repeat: repeat:
new_ie = kmem_cache_alloc(winode_slab, GFP_NOFS); new_ie = kmem_cache_alloc(winode_slab, GFP_NOFS);
......
...@@ -109,12 +109,6 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) ...@@ -109,12 +109,6 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
ret = do_read_inode(inode); ret = do_read_inode(inode);
if (ret) if (ret)
goto bad_inode; goto bad_inode;
if (!sbi->por_doing && inode->i_nlink == 0) {
ret = -ENOENT;
goto bad_inode;
}
make_now: make_now:
if (ino == F2FS_NODE_INO(sbi)) { if (ino == F2FS_NODE_INO(sbi)) {
inode->i_mapping->a_ops = &f2fs_node_aops; inode->i_mapping->a_ops = &f2fs_node_aops;
...@@ -130,8 +124,7 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) ...@@ -130,8 +124,7 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
inode->i_op = &f2fs_dir_inode_operations; inode->i_op = &f2fs_dir_inode_operations;
inode->i_fop = &f2fs_dir_operations; inode->i_fop = &f2fs_dir_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER_MOVABLE | mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO);
__GFP_ZERO);
} else if (S_ISLNK(inode->i_mode)) { } else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &f2fs_symlink_inode_operations; inode->i_op = &f2fs_symlink_inode_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
...@@ -199,6 +192,7 @@ void update_inode(struct inode *inode, struct page *node_page) ...@@ -199,6 +192,7 @@ void update_inode(struct inode *inode, struct page *node_page)
set_cold_node(inode, node_page); set_cold_node(inode, node_page);
set_page_dirty(node_page); set_page_dirty(node_page);
clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
} }
int update_inode_page(struct inode *inode) int update_inode_page(struct inode *inode)
...@@ -224,6 +218,9 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) ...@@ -224,6 +218,9 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
inode->i_ino == F2FS_META_INO(sbi)) inode->i_ino == F2FS_META_INO(sbi))
return 0; return 0;
if (!is_inode_flag_set(F2FS_I(inode), FI_DIRTY_INODE))
return 0;
if (wbc) if (wbc)
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
......
...@@ -112,7 +112,7 @@ static inline void set_cold_files(struct f2fs_sb_info *sbi, struct inode *inode, ...@@ -112,7 +112,7 @@ static inline void set_cold_files(struct f2fs_sb_info *sbi, struct inode *inode,
int count = le32_to_cpu(sbi->raw_super->extension_count); int count = le32_to_cpu(sbi->raw_super->extension_count);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
if (is_multimedia_file(name, extlist[i])) { if (is_multimedia_file(name, extlist[i])) {
set_cold_file(inode); file_set_cold(inode);
break; break;
} }
} }
...@@ -149,8 +149,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -149,8 +149,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
alloc_nid_done(sbi, ino); alloc_nid_done(sbi, ino);
if (!sbi->por_doing) d_instantiate(dentry, inode);
d_instantiate(dentry, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
return 0; return 0;
out: out:
...@@ -173,7 +172,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, ...@@ -173,7 +172,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
f2fs_balance_fs(sbi); f2fs_balance_fs(sbi);
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
atomic_inc(&inode->i_count); ihold(inode);
set_inode_flag(F2FS_I(inode), FI_INC_LINK); set_inode_flag(F2FS_I(inode), FI_INC_LINK);
ilock = mutex_lock_op(sbi); ilock = mutex_lock_op(sbi);
...@@ -182,17 +181,10 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, ...@@ -182,17 +181,10 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
if (err) if (err)
goto out; goto out;
/*
* This file should be checkpointed during fsync.
* We lost i_pino from now on.
*/
set_cp_file(inode);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
return 0; return 0;
out: out:
clear_inode_flag(F2FS_I(inode), FI_INC_LINK); clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
make_bad_inode(inode);
iput(inode); iput(inode);
return err; return err;
} }
...@@ -498,6 +490,7 @@ const struct inode_operations f2fs_dir_inode_operations = { ...@@ -498,6 +490,7 @@ const struct inode_operations f2fs_dir_inode_operations = {
.rmdir = f2fs_rmdir, .rmdir = f2fs_rmdir,
.mknod = f2fs_mknod, .mknod = f2fs_mknod,
.rename = f2fs_rename, .rename = f2fs_rename,
.getattr = f2fs_getattr,
.setattr = f2fs_setattr, .setattr = f2fs_setattr,
.get_acl = f2fs_get_acl, .get_acl = f2fs_get_acl,
#ifdef CONFIG_F2FS_FS_XATTR #ifdef CONFIG_F2FS_FS_XATTR
...@@ -512,6 +505,7 @@ const struct inode_operations f2fs_symlink_inode_operations = { ...@@ -512,6 +505,7 @@ const struct inode_operations f2fs_symlink_inode_operations = {
.readlink = generic_readlink, .readlink = generic_readlink,
.follow_link = page_follow_link_light, .follow_link = page_follow_link_light,
.put_link = page_put_link, .put_link = page_put_link,
.getattr = f2fs_getattr,
.setattr = f2fs_setattr, .setattr = f2fs_setattr,
#ifdef CONFIG_F2FS_FS_XATTR #ifdef CONFIG_F2FS_FS_XATTR
.setxattr = generic_setxattr, .setxattr = generic_setxattr,
...@@ -522,6 +516,7 @@ const struct inode_operations f2fs_symlink_inode_operations = { ...@@ -522,6 +516,7 @@ const struct inode_operations f2fs_symlink_inode_operations = {
}; };
const struct inode_operations f2fs_special_inode_operations = { const struct inode_operations f2fs_special_inode_operations = {
.getattr = f2fs_getattr,
.setattr = f2fs_setattr, .setattr = f2fs_setattr,
.get_acl = f2fs_get_acl, .get_acl = f2fs_get_acl,
#ifdef CONFIG_F2FS_FS_XATTR #ifdef CONFIG_F2FS_FS_XATTR
......
...@@ -408,10 +408,13 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) ...@@ -408,10 +408,13 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
level = get_node_path(index, offset, noffset); level = get_node_path(index, offset, noffset);
nids[0] = dn->inode->i_ino; nids[0] = dn->inode->i_ino;
npage[0] = get_node_page(sbi, nids[0]); npage[0] = dn->inode_page;
if (IS_ERR(npage[0]))
return PTR_ERR(npage[0]);
if (!npage[0]) {
npage[0] = get_node_page(sbi, nids[0]);
if (IS_ERR(npage[0]))
return PTR_ERR(npage[0]);
}
parent = npage[0]; parent = npage[0];
if (level != 0) if (level != 0)
nids[1] = get_nid(parent, offset[0], true); nids[1] = get_nid(parent, offset[0], true);
...@@ -430,7 +433,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) ...@@ -430,7 +433,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode)
} }
dn->nid = nids[i]; dn->nid = nids[i];
npage[i] = new_node_page(dn, noffset[i]); npage[i] = new_node_page(dn, noffset[i], NULL);
if (IS_ERR(npage[i])) { if (IS_ERR(npage[i])) {
alloc_nid_failed(sbi, nids[i]); alloc_nid_failed(sbi, nids[i]);
err = PTR_ERR(npage[i]); err = PTR_ERR(npage[i]);
...@@ -803,22 +806,19 @@ int remove_inode_page(struct inode *inode) ...@@ -803,22 +806,19 @@ int remove_inode_page(struct inode *inode)
return 0; return 0;
} }
int new_inode_page(struct inode *inode, const struct qstr *name) struct page *new_inode_page(struct inode *inode, const struct qstr *name)
{ {
struct page *page;
struct dnode_of_data dn; struct dnode_of_data dn;
/* allocate inode page for new inode */ /* allocate inode page for new inode */
set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino);
page = new_node_page(&dn, 0);
init_dent_inode(name, page); /* caller should f2fs_put_page(page, 1); */
if (IS_ERR(page)) return new_node_page(&dn, 0, NULL);
return PTR_ERR(page);
f2fs_put_page(page, 1);
return 0;
} }
struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs) struct page *new_node_page(struct dnode_of_data *dn,
unsigned int ofs, struct page *ipage)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb);
struct address_space *mapping = sbi->node_inode->i_mapping; struct address_space *mapping = sbi->node_inode->i_mapping;
...@@ -851,7 +851,10 @@ struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs) ...@@ -851,7 +851,10 @@ struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs)
set_cold_node(dn->inode, page); set_cold_node(dn->inode, page);
dn->node_page = page; dn->node_page = page;
sync_inode_page(dn); if (ipage)
update_inode(dn->inode, ipage);
else
sync_inode_page(dn);
set_page_dirty(page); set_page_dirty(page);
if (ofs == 0) if (ofs == 0)
inc_valid_inode_count(sbi); inc_valid_inode_count(sbi);
...@@ -1493,9 +1496,10 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) ...@@ -1493,9 +1496,10 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)
new_ni = old_ni; new_ni = old_ni;
new_ni.ino = ino; new_ni.ino = ino;
if (!inc_valid_node_count(sbi, NULL, 1))
WARN_ON(1);
set_node_addr(sbi, &new_ni, NEW_ADDR); set_node_addr(sbi, &new_ni, NEW_ADDR);
inc_valid_inode_count(sbi); inc_valid_inode_count(sbi);
f2fs_put_page(ipage, 1); f2fs_put_page(ipage, 1);
return 0; return 0;
} }
......
...@@ -275,25 +275,27 @@ static inline nid_t get_nid(struct page *p, int off, bool i) ...@@ -275,25 +275,27 @@ static inline nid_t get_nid(struct page *p, int off, bool i)
* - Mark cold node blocks in their node footer * - Mark cold node blocks in their node footer
* - Mark cold data pages in page cache * - Mark cold data pages in page cache
*/ */
static inline int is_cold_file(struct inode *inode) static inline int is_file(struct inode *inode, int type)
{ {
return F2FS_I(inode)->i_advise & FADVISE_COLD_BIT; return F2FS_I(inode)->i_advise & type;
} }
static inline void set_cold_file(struct inode *inode) static inline void set_file(struct inode *inode, int type)
{ {
F2FS_I(inode)->i_advise |= FADVISE_COLD_BIT; F2FS_I(inode)->i_advise |= type;
} }
static inline int is_cp_file(struct inode *inode) static inline void clear_file(struct inode *inode, int type)
{ {
return F2FS_I(inode)->i_advise & FADVISE_CP_BIT; F2FS_I(inode)->i_advise &= ~type;
} }
static inline void set_cp_file(struct inode *inode) #define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT)
{ #define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT)
F2FS_I(inode)->i_advise |= FADVISE_CP_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) static inline int is_cold_data(struct page *page)
{ {
...@@ -310,29 +312,16 @@ static inline void clear_cold_data(struct page *page) ...@@ -310,29 +312,16 @@ static inline void clear_cold_data(struct page *page)
ClearPageChecked(page); ClearPageChecked(page);
} }
static inline int is_cold_node(struct page *page) static inline int is_node(struct page *page, int type)
{ {
void *kaddr = page_address(page); void *kaddr = page_address(page);
struct f2fs_node *rn = (struct f2fs_node *)kaddr; struct f2fs_node *rn = (struct f2fs_node *)kaddr;
unsigned int flag = le32_to_cpu(rn->footer.flag); return le32_to_cpu(rn->footer.flag) & (1 << type);
return flag & (0x1 << COLD_BIT_SHIFT);
} }
static inline unsigned char is_fsync_dnode(struct page *page) #define is_cold_node(page) is_node(page, COLD_BIT_SHIFT)
{ #define is_fsync_dnode(page) is_node(page, FSYNC_BIT_SHIFT)
void *kaddr = page_address(page); #define is_dent_dnode(page) is_node(page, DENT_BIT_SHIFT)
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
unsigned int flag = le32_to_cpu(rn->footer.flag);
return flag & (0x1 << FSYNC_BIT_SHIFT);
}
static inline unsigned char is_dent_dnode(struct page *page)
{
void *kaddr = page_address(page);
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
unsigned int flag = le32_to_cpu(rn->footer.flag);
return flag & (0x1 << DENT_BIT_SHIFT);
}
static inline void set_cold_node(struct inode *inode, struct page *page) static inline void set_cold_node(struct inode *inode, struct page *page)
{ {
...@@ -346,26 +335,15 @@ static inline void set_cold_node(struct inode *inode, struct page *page) ...@@ -346,26 +335,15 @@ static inline void set_cold_node(struct inode *inode, struct page *page)
rn->footer.flag = cpu_to_le32(flag); rn->footer.flag = cpu_to_le32(flag);
} }
static inline void set_fsync_mark(struct page *page, int mark) static inline void set_mark(struct page *page, int mark, int type)
{ {
void *kaddr = page_address(page); struct f2fs_node *rn = (struct f2fs_node *)page_address(page);
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
unsigned int flag = le32_to_cpu(rn->footer.flag);
if (mark)
flag |= (0x1 << FSYNC_BIT_SHIFT);
else
flag &= ~(0x1 << FSYNC_BIT_SHIFT);
rn->footer.flag = cpu_to_le32(flag);
}
static inline void set_dentry_mark(struct page *page, int mark)
{
void *kaddr = page_address(page);
struct f2fs_node *rn = (struct f2fs_node *)kaddr;
unsigned int flag = le32_to_cpu(rn->footer.flag); unsigned int flag = le32_to_cpu(rn->footer.flag);
if (mark) if (mark)
flag |= (0x1 << DENT_BIT_SHIFT); flag |= (0x1 << type);
else else
flag &= ~(0x1 << DENT_BIT_SHIFT); flag &= ~(0x1 << type);
rn->footer.flag = cpu_to_le32(flag); rn->footer.flag = cpu_to_le32(flag);
} }
#define set_dentry_mark(page, mark) set_mark(page, mark, DENT_BIT_SHIFT)
#define set_fsync_mark(page, mark) set_mark(page, mark, FSYNC_BIT_SHIFT)
...@@ -40,36 +40,54 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, ...@@ -40,36 +40,54 @@ static struct fsync_inode_entry *get_fsync_inode(struct list_head *head,
static int recover_dentry(struct page *ipage, struct inode *inode) static int recover_dentry(struct page *ipage, struct inode *inode)
{ {
struct f2fs_node *raw_node = (struct f2fs_node *)kmap(ipage); void *kaddr = page_address(ipage);
struct f2fs_node *raw_node = (struct f2fs_node *)kaddr;
struct f2fs_inode *raw_inode = &(raw_node->i); struct f2fs_inode *raw_inode = &(raw_node->i);
struct qstr name; nid_t pino = le32_to_cpu(raw_inode->i_pino);
struct f2fs_dir_entry *de; struct f2fs_dir_entry *de;
struct qstr name;
struct page *page; struct page *page;
struct inode *dir; struct inode *dir, *einode;
int err = 0; int err = 0;
if (!is_dent_dnode(ipage)) dir = check_dirty_dir_inode(F2FS_SB(inode->i_sb), pino);
goto out; if (!dir) {
dir = f2fs_iget(inode->i_sb, pino);
dir = f2fs_iget(inode->i_sb, le32_to_cpu(raw_inode->i_pino)); if (IS_ERR(dir)) {
if (IS_ERR(dir)) { err = PTR_ERR(dir);
err = PTR_ERR(dir); goto out;
goto out; }
set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT);
add_dirty_dir_inode(dir);
} }
name.len = le32_to_cpu(raw_inode->i_namelen); name.len = le32_to_cpu(raw_inode->i_namelen);
name.name = raw_inode->i_name; name.name = raw_inode->i_name;
retry:
de = f2fs_find_entry(dir, &name, &page); de = f2fs_find_entry(dir, &name, &page);
if (de) { if (de && inode->i_ino == le32_to_cpu(de->ino)) {
kunmap(page); kunmap(page);
f2fs_put_page(page, 0); f2fs_put_page(page, 0);
} else { goto out;
err = __f2fs_add_link(dir, &name, inode); }
if (de) {
einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino));
if (IS_ERR(einode)) {
WARN_ON(1);
if (PTR_ERR(einode) == -ENOENT)
err = -EEXIST;
goto out;
}
f2fs_delete_entry(de, page, einode);
iput(einode);
goto retry;
} }
iput(dir); err = __f2fs_add_link(dir, &name, inode);
out: out:
kunmap(ipage); f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode and its dentry: "
"ino = %x, name = %s, dir = %lx, err = %d",
ino_of_node(ipage), raw_inode->i_name,
IS_ERR(dir) ? 0 : dir->i_ino, err);
return err; return err;
} }
...@@ -79,6 +97,9 @@ static int recover_inode(struct inode *inode, struct page *node_page) ...@@ -79,6 +97,9 @@ static int recover_inode(struct inode *inode, struct page *node_page)
struct f2fs_node *raw_node = (struct f2fs_node *)kaddr; struct f2fs_node *raw_node = (struct f2fs_node *)kaddr;
struct f2fs_inode *raw_inode = &(raw_node->i); struct f2fs_inode *raw_inode = &(raw_node->i);
if (!IS_INODE(node_page))
return 0;
inode->i_mode = le16_to_cpu(raw_inode->i_mode); inode->i_mode = le16_to_cpu(raw_inode->i_mode);
i_size_write(inode, le64_to_cpu(raw_inode->i_size)); i_size_write(inode, le64_to_cpu(raw_inode->i_size));
inode->i_atime.tv_sec = le64_to_cpu(raw_inode->i_mtime); inode->i_atime.tv_sec = le64_to_cpu(raw_inode->i_mtime);
...@@ -88,7 +109,12 @@ static int recover_inode(struct inode *inode, struct page *node_page) ...@@ -88,7 +109,12 @@ static int recover_inode(struct inode *inode, struct page *node_page)
inode->i_ctime.tv_nsec = le32_to_cpu(raw_inode->i_ctime_nsec); inode->i_ctime.tv_nsec = le32_to_cpu(raw_inode->i_ctime_nsec);
inode->i_mtime.tv_nsec = le32_to_cpu(raw_inode->i_mtime_nsec); inode->i_mtime.tv_nsec = le32_to_cpu(raw_inode->i_mtime_nsec);
return recover_dentry(node_page, inode); if (is_dent_dnode(node_page))
return recover_dentry(node_page, inode);
f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s",
ino_of_node(node_page), raw_inode->i_name);
return 0;
} }
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
...@@ -119,14 +145,13 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) ...@@ -119,14 +145,13 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
lock_page(page); lock_page(page);
if (cp_ver != cpver_of_node(page)) if (cp_ver != cpver_of_node(page))
goto unlock_out; break;
if (!is_fsync_dnode(page)) if (!is_fsync_dnode(page))
goto next; goto next;
entry = get_fsync_inode(head, ino_of_node(page)); entry = get_fsync_inode(head, ino_of_node(page));
if (entry) { if (entry) {
entry->blkaddr = blkaddr;
if (IS_INODE(page) && is_dent_dnode(page)) if (IS_INODE(page) && is_dent_dnode(page))
set_inode_flag(F2FS_I(entry->inode), set_inode_flag(F2FS_I(entry->inode),
FI_INC_LINK); FI_INC_LINK);
...@@ -134,48 +159,40 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) ...@@ -134,48 +159,40 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
if (IS_INODE(page) && is_dent_dnode(page)) { if (IS_INODE(page) && is_dent_dnode(page)) {
err = recover_inode_page(sbi, page); err = recover_inode_page(sbi, page);
if (err) if (err)
goto unlock_out; break;
} }
/* add this fsync inode to the list */ /* add this fsync inode to the list */
entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS); entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS);
if (!entry) { if (!entry) {
err = -ENOMEM; err = -ENOMEM;
goto unlock_out; break;
} }
entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); entry->inode = f2fs_iget(sbi->sb, ino_of_node(page));
if (IS_ERR(entry->inode)) { if (IS_ERR(entry->inode)) {
err = PTR_ERR(entry->inode); err = PTR_ERR(entry->inode);
kmem_cache_free(fsync_entry_slab, entry); kmem_cache_free(fsync_entry_slab, entry);
goto unlock_out; break;
} }
list_add_tail(&entry->list, head); list_add_tail(&entry->list, head);
entry->blkaddr = blkaddr;
}
if (IS_INODE(page)) {
err = recover_inode(entry->inode, page);
if (err == -ENOENT) {
goto next;
} else if (err) {
err = -EINVAL;
goto unlock_out;
}
} }
entry->blkaddr = blkaddr;
err = recover_inode(entry->inode, page);
if (err && err != -ENOENT)
break;
next: next:
/* check next segment */ /* check next segment */
blkaddr = next_blkaddr_of_node(page); blkaddr = next_blkaddr_of_node(page);
} }
unlock_out:
unlock_page(page); unlock_page(page);
out: out:
__free_pages(page, 0); __free_pages(page, 0);
return err; return err;
} }
static void destroy_fsync_dnodes(struct f2fs_sb_info *sbi, static void destroy_fsync_dnodes(struct list_head *head)
struct list_head *head)
{ {
struct fsync_inode_entry *entry, *tmp; struct fsync_inode_entry *entry, *tmp;
...@@ -186,15 +203,15 @@ static void destroy_fsync_dnodes(struct f2fs_sb_info *sbi, ...@@ -186,15 +203,15 @@ static void destroy_fsync_dnodes(struct f2fs_sb_info *sbi,
} }
} }
static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi, static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
block_t blkaddr) block_t blkaddr, struct dnode_of_data *dn)
{ {
struct seg_entry *sentry; struct seg_entry *sentry;
unsigned int segno = GET_SEGNO(sbi, blkaddr); unsigned int segno = GET_SEGNO(sbi, blkaddr);
unsigned short blkoff = GET_SEGOFF_FROM_SEG0(sbi, blkaddr) & unsigned short blkoff = GET_SEGOFF_FROM_SEG0(sbi, blkaddr) &
(sbi->blocks_per_seg - 1); (sbi->blocks_per_seg - 1);
struct f2fs_summary sum; struct f2fs_summary sum;
nid_t ino; nid_t ino, nid;
void *kaddr; void *kaddr;
struct inode *inode; struct inode *inode;
struct page *node_page; struct page *node_page;
...@@ -203,7 +220,7 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi, ...@@ -203,7 +220,7 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
sentry = get_seg_entry(sbi, segno); sentry = get_seg_entry(sbi, segno);
if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) if (!f2fs_test_bit(blkoff, sentry->cur_valid_map))
return; return 0;
/* Get the previous summary */ /* Get the previous summary */
for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) { for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) {
...@@ -222,20 +239,39 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi, ...@@ -222,20 +239,39 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi,
f2fs_put_page(sum_page, 1); f2fs_put_page(sum_page, 1);
} }
/* 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;
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;
} 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;
}
/* Get the node page */ /* Get the node page */
node_page = get_node_page(sbi, le32_to_cpu(sum.nid)); node_page = get_node_page(sbi, nid);
if (IS_ERR(node_page))
return PTR_ERR(node_page);
bidx = start_bidx_of_node(ofs_of_node(node_page)) + bidx = start_bidx_of_node(ofs_of_node(node_page)) +
le16_to_cpu(sum.ofs_in_node); le16_to_cpu(sum.ofs_in_node);
ino = ino_of_node(node_page); ino = ino_of_node(node_page);
f2fs_put_page(node_page, 1); f2fs_put_page(node_page, 1);
/* Deallocate previous index in the node page */ /* Deallocate previous index in the node page */
inode = f2fs_iget(sbi->sb, ino); inode = f2fs_iget(sbi->sb, ino);
if (IS_ERR(inode)) if (IS_ERR(inode))
return; return PTR_ERR(inode);
truncate_hole(inode, bidx, bidx + 1); truncate_hole(inode, bidx, bidx + 1);
iput(inode); iput(inode);
return 0;
} }
static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
...@@ -245,7 +281,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, ...@@ -245,7 +281,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
struct dnode_of_data dn; struct dnode_of_data dn;
struct f2fs_summary sum; struct f2fs_summary sum;
struct node_info ni; struct node_info ni;
int err = 0; int err = 0, recovered = 0;
int ilock; int ilock;
start = start_bidx_of_node(ofs_of_node(page)); start = start_bidx_of_node(ofs_of_node(page));
...@@ -283,13 +319,16 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, ...@@ -283,13 +319,16 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
} }
/* Check the previous node page having this index */ /* Check the previous node page having this index */
check_index_in_prev_nodes(sbi, dest); err = check_index_in_prev_nodes(sbi, dest, &dn);
if (err)
goto err;
set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
/* write dummy data page */ /* write dummy data page */
recover_data_page(sbi, NULL, &sum, src, dest); recover_data_page(sbi, NULL, &sum, src, dest);
update_extent_cache(dest, &dn); update_extent_cache(dest, &dn);
recovered++;
} }
dn.ofs_in_node++; dn.ofs_in_node++;
} }
...@@ -305,9 +344,14 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, ...@@ -305,9 +344,14 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
set_page_dirty(dn.node_page); set_page_dirty(dn.node_page);
recover_node_page(sbi, dn.node_page, &sum, &ni, blkaddr); recover_node_page(sbi, dn.node_page, &sum, &ni, blkaddr);
err:
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
mutex_unlock_op(sbi, ilock); mutex_unlock_op(sbi, ilock);
return 0;
f2fs_msg(sbi->sb, KERN_NOTICE, "recover_data: ino = %lx, "
"recovered_data = %d blocks, err = %d",
inode->i_ino, recovered, err);
return err;
} }
static int recover_data(struct f2fs_sb_info *sbi, static int recover_data(struct f2fs_sb_info *sbi,
...@@ -340,7 +384,7 @@ static int recover_data(struct f2fs_sb_info *sbi, ...@@ -340,7 +384,7 @@ static int recover_data(struct f2fs_sb_info *sbi,
lock_page(page); lock_page(page);
if (cp_ver != cpver_of_node(page)) if (cp_ver != cpver_of_node(page))
goto unlock_out; break;
entry = get_fsync_inode(head, ino_of_node(page)); entry = get_fsync_inode(head, ino_of_node(page));
if (!entry) if (!entry)
...@@ -348,7 +392,7 @@ static int recover_data(struct f2fs_sb_info *sbi, ...@@ -348,7 +392,7 @@ static int recover_data(struct f2fs_sb_info *sbi,
err = do_recover_data(sbi, entry->inode, page, blkaddr); err = do_recover_data(sbi, entry->inode, page, blkaddr);
if (err) if (err)
goto out; break;
if (entry->blkaddr == blkaddr) { if (entry->blkaddr == blkaddr) {
iput(entry->inode); iput(entry->inode);
...@@ -359,7 +403,6 @@ static int recover_data(struct f2fs_sb_info *sbi, ...@@ -359,7 +403,6 @@ static int recover_data(struct f2fs_sb_info *sbi,
/* check next segment */ /* check next segment */
blkaddr = next_blkaddr_of_node(page); blkaddr = next_blkaddr_of_node(page);
} }
unlock_out:
unlock_page(page); unlock_page(page);
out: out:
__free_pages(page, 0); __free_pages(page, 0);
...@@ -382,6 +425,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi) ...@@ -382,6 +425,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
INIT_LIST_HEAD(&inode_list); INIT_LIST_HEAD(&inode_list);
/* step #1: find fsynced inode numbers */ /* step #1: find fsynced inode numbers */
sbi->por_doing = 1;
err = find_fsync_dnodes(sbi, &inode_list); err = find_fsync_dnodes(sbi, &inode_list);
if (err) if (err)
goto out; goto out;
...@@ -390,13 +434,13 @@ int recover_fsync_data(struct f2fs_sb_info *sbi) ...@@ -390,13 +434,13 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
goto out; goto out;
/* step #2: recover data */ /* step #2: recover data */
sbi->por_doing = 1;
err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE); err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE);
sbi->por_doing = 0;
BUG_ON(!list_empty(&inode_list)); BUG_ON(!list_empty(&inode_list));
out: out:
destroy_fsync_dnodes(sbi, &inode_list); destroy_fsync_dnodes(&inode_list);
kmem_cache_destroy(fsync_entry_slab); kmem_cache_destroy(fsync_entry_slab);
write_checkpoint(sbi, false); sbi->por_doing = 0;
if (!err)
write_checkpoint(sbi, false);
return err; return err;
} }
...@@ -94,7 +94,7 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, ...@@ -94,7 +94,7 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
* Adding dirty entry into seglist is not critical operation. * Adding dirty entry into seglist is not critical operation.
* If a given segment is one of current working segments, it won't be added. * If a given segment is one of current working segments, it won't be added.
*/ */
void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned short valid_blocks; unsigned short valid_blocks;
...@@ -126,17 +126,16 @@ void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) ...@@ -126,17 +126,16 @@ void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned int segno, offset = 0; unsigned int segno = -1;
unsigned int total_segs = TOTAL_SEGS(sbi); unsigned int total_segs = TOTAL_SEGS(sbi);
mutex_lock(&dirty_i->seglist_lock); mutex_lock(&dirty_i->seglist_lock);
while (1) { while (1) {
segno = find_next_bit(dirty_i->dirty_segmap[PRE], total_segs, segno = find_next_bit(dirty_i->dirty_segmap[PRE], total_segs,
offset); segno + 1);
if (segno >= total_segs) if (segno >= total_segs)
break; break;
__set_test_and_free(sbi, segno); __set_test_and_free(sbi, segno);
offset = segno + 1;
} }
mutex_unlock(&dirty_i->seglist_lock); mutex_unlock(&dirty_i->seglist_lock);
} }
...@@ -144,17 +143,16 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) ...@@ -144,17 +143,16 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
void clear_prefree_segments(struct f2fs_sb_info *sbi) void clear_prefree_segments(struct f2fs_sb_info *sbi)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned int segno, offset = 0; unsigned int segno = -1;
unsigned int total_segs = TOTAL_SEGS(sbi); unsigned int total_segs = TOTAL_SEGS(sbi);
mutex_lock(&dirty_i->seglist_lock); mutex_lock(&dirty_i->seglist_lock);
while (1) { while (1) {
segno = find_next_bit(dirty_i->dirty_segmap[PRE], total_segs, segno = find_next_bit(dirty_i->dirty_segmap[PRE], total_segs,
offset); segno + 1);
if (segno >= total_segs) if (segno >= total_segs)
break; break;
offset = segno + 1;
if (test_and_clear_bit(segno, dirty_i->dirty_segmap[PRE])) if (test_and_clear_bit(segno, dirty_i->dirty_segmap[PRE]))
dirty_i->nr_dirty[PRE]--; dirty_i->nr_dirty[PRE]--;
...@@ -257,11 +255,11 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) ...@@ -257,11 +255,11 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
* This function should be resided under the curseg_mutex lock * This function should be resided under the curseg_mutex lock
*/ */
static void __add_sum_entry(struct f2fs_sb_info *sbi, int type, static void __add_sum_entry(struct f2fs_sb_info *sbi, int type,
struct f2fs_summary *sum, unsigned short offset) struct f2fs_summary *sum)
{ {
struct curseg_info *curseg = CURSEG_I(sbi, type); struct curseg_info *curseg = CURSEG_I(sbi, type);
void *addr = curseg->sum_blk; void *addr = curseg->sum_blk;
addr += offset * sizeof(struct f2fs_summary); addr += curseg->next_blkoff * sizeof(struct f2fs_summary);
memcpy(addr, sum, sizeof(struct f2fs_summary)); memcpy(addr, sum, sizeof(struct f2fs_summary));
return; return;
} }
...@@ -311,64 +309,14 @@ static void write_sum_page(struct f2fs_sb_info *sbi, ...@@ -311,64 +309,14 @@ static void write_sum_page(struct f2fs_sb_info *sbi,
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
} }
static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, int type)
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
unsigned long *prefree_segmap = dirty_i->dirty_segmap[PRE];
unsigned int segno;
unsigned int ofs = 0;
/*
* If there is not enough reserved sections,
* we should not reuse prefree segments.
*/
if (has_not_enough_free_secs(sbi, 0))
return NULL_SEGNO;
/*
* NODE page should not reuse prefree segment,
* since those information is used for SPOR.
*/
if (IS_NODESEG(type))
return NULL_SEGNO;
next:
segno = find_next_bit(prefree_segmap, TOTAL_SEGS(sbi), ofs);
ofs += sbi->segs_per_sec;
if (segno < TOTAL_SEGS(sbi)) {
int i;
/* skip intermediate segments in a section */
if (segno % sbi->segs_per_sec)
goto next;
/* skip if the section is currently used */
if (sec_usage_check(sbi, GET_SECNO(sbi, segno)))
goto next;
/* skip if whole section is not prefree */
for (i = 1; i < sbi->segs_per_sec; i++)
if (!test_bit(segno + i, prefree_segmap))
goto next;
/* skip if whole section was not free at the last checkpoint */
for (i = 0; i < sbi->segs_per_sec; i++)
if (get_seg_entry(sbi, segno + i)->ckpt_valid_blocks)
goto next;
return segno;
}
return NULL_SEGNO;
}
static int is_next_segment_free(struct f2fs_sb_info *sbi, int type) static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
{ {
struct curseg_info *curseg = CURSEG_I(sbi, type); struct curseg_info *curseg = CURSEG_I(sbi, type);
unsigned int segno = curseg->segno; unsigned int segno = curseg->segno + 1;
struct free_segmap_info *free_i = FREE_I(sbi); struct free_segmap_info *free_i = FREE_I(sbi);
if (segno + 1 < TOTAL_SEGS(sbi) && (segno + 1) % sbi->segs_per_sec) if (segno < TOTAL_SEGS(sbi) && segno % sbi->segs_per_sec)
return !test_bit(segno + 1, free_i->free_segmap); return !test_bit(segno, free_i->free_segmap);
return 0; return 0;
} }
...@@ -495,7 +443,7 @@ static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec) ...@@ -495,7 +443,7 @@ static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec)
int dir = ALLOC_LEFT; int dir = ALLOC_LEFT;
write_sum_page(sbi, curseg->sum_blk, write_sum_page(sbi, curseg->sum_blk,
GET_SUM_BLOCK(sbi, curseg->segno)); GET_SUM_BLOCK(sbi, segno));
if (type == CURSEG_WARM_DATA || type == CURSEG_COLD_DATA) if (type == CURSEG_WARM_DATA || type == CURSEG_COLD_DATA)
dir = ALLOC_RIGHT; dir = ALLOC_RIGHT;
...@@ -599,11 +547,7 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi, ...@@ -599,11 +547,7 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
goto out; goto out;
} }
curseg->next_segno = check_prefree_segments(sbi, type); if (type == CURSEG_WARM_NODE)
if (curseg->next_segno != NULL_SEGNO)
change_curseg(sbi, type, false);
else if (type == CURSEG_WARM_NODE)
new_curseg(sbi, type, false); new_curseg(sbi, type, false);
else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type)) else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type))
new_curseg(sbi, type, false); new_curseg(sbi, type, false);
...@@ -612,7 +556,10 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi, ...@@ -612,7 +556,10 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
else else
new_curseg(sbi, type, false); new_curseg(sbi, type, false);
out: out:
#ifdef CONFIG_F2FS_STAT_FS
sbi->segment_count[curseg->alloc_type]++; sbi->segment_count[curseg->alloc_type]++;
#endif
return;
} }
void allocate_new_segments(struct f2fs_sb_info *sbi) void allocate_new_segments(struct f2fs_sb_info *sbi)
...@@ -795,7 +742,7 @@ static int __get_segment_type_6(struct page *page, enum page_type p_type) ...@@ -795,7 +742,7 @@ static int __get_segment_type_6(struct page *page, enum page_type p_type)
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
return CURSEG_HOT_DATA; return CURSEG_HOT_DATA;
else if (is_cold_data(page) || is_cold_file(inode)) else if (is_cold_data(page) || file_is_cold(inode))
return CURSEG_COLD_DATA; return CURSEG_COLD_DATA;
else else
return CURSEG_WARM_DATA; return CURSEG_WARM_DATA;
...@@ -844,11 +791,13 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, ...@@ -844,11 +791,13 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page,
* because, this function updates a summary entry in the * because, this function updates a summary entry in the
* current summary block. * current summary block.
*/ */
__add_sum_entry(sbi, type, sum, curseg->next_blkoff); __add_sum_entry(sbi, type, sum);
mutex_lock(&sit_i->sentry_lock); mutex_lock(&sit_i->sentry_lock);
__refresh_next_blkoff(sbi, curseg); __refresh_next_blkoff(sbi, curseg);
#ifdef CONFIG_F2FS_STAT_FS
sbi->block_count[curseg->alloc_type]++; sbi->block_count[curseg->alloc_type]++;
#endif
/* /*
* SIT information should be updated before segment allocation, * SIT information should be updated before segment allocation,
...@@ -943,7 +892,7 @@ void recover_data_page(struct f2fs_sb_info *sbi, ...@@ -943,7 +892,7 @@ void recover_data_page(struct f2fs_sb_info *sbi,
curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) & curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) &
(sbi->blocks_per_seg - 1); (sbi->blocks_per_seg - 1);
__add_sum_entry(sbi, type, sum, curseg->next_blkoff); __add_sum_entry(sbi, type, sum);
refresh_sit_entry(sbi, old_blkaddr, new_blkaddr); refresh_sit_entry(sbi, old_blkaddr, new_blkaddr);
...@@ -980,7 +929,7 @@ void rewrite_node_page(struct f2fs_sb_info *sbi, ...@@ -980,7 +929,7 @@ void rewrite_node_page(struct f2fs_sb_info *sbi,
} }
curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) & curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) &
(sbi->blocks_per_seg - 1); (sbi->blocks_per_seg - 1);
__add_sum_entry(sbi, type, sum, curseg->next_blkoff); __add_sum_entry(sbi, type, sum);
/* change the current log to the next block addr in advance */ /* change the current log to the next block addr in advance */
if (next_segno != segno) { if (next_segno != segno) {
...@@ -1579,13 +1528,13 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi) ...@@ -1579,13 +1528,13 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
struct free_segmap_info *free_i = FREE_I(sbi); struct free_segmap_info *free_i = FREE_I(sbi);
unsigned int segno = 0, offset = 0; unsigned int segno = 0, offset = 0, total_segs = TOTAL_SEGS(sbi);
unsigned short valid_blocks; unsigned short valid_blocks;
while (segno < TOTAL_SEGS(sbi)) { while (1) {
/* find dirty segment based on free segmap */ /* find dirty segment based on free segmap */
segno = find_next_inuse(free_i, TOTAL_SEGS(sbi), offset); segno = find_next_inuse(free_i, total_segs, offset);
if (segno >= TOTAL_SEGS(sbi)) if (segno >= total_segs)
break; break;
offset = segno + 1; offset = segno + 1;
valid_blocks = get_valid_blocks(sbi, segno, 0); valid_blocks = get_valid_blocks(sbi, segno, 0);
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
static struct kmem_cache *f2fs_inode_cachep; static struct kmem_cache *f2fs_inode_cachep;
enum { enum {
Opt_gc_background_off, Opt_gc_background,
Opt_disable_roll_forward, Opt_disable_roll_forward,
Opt_discard, Opt_discard,
Opt_noheap, Opt_noheap,
...@@ -46,7 +46,7 @@ enum { ...@@ -46,7 +46,7 @@ enum {
}; };
static match_table_t f2fs_tokens = { static match_table_t f2fs_tokens = {
{Opt_gc_background_off, "background_gc_off"}, {Opt_gc_background, "background_gc=%s"},
{Opt_disable_roll_forward, "disable_roll_forward"}, {Opt_disable_roll_forward, "disable_roll_forward"},
{Opt_discard, "discard"}, {Opt_discard, "discard"},
{Opt_noheap, "no_heap"}, {Opt_noheap, "no_heap"},
...@@ -76,6 +76,91 @@ static void init_once(void *foo) ...@@ -76,6 +76,91 @@ static void init_once(void *foo)
inode_init_once(&fi->vfs_inode); inode_init_once(&fi->vfs_inode);
} }
static int parse_options(struct super_block *sb, char *options)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
substring_t args[MAX_OPT_ARGS];
char *p, *name;
int arg = 0;
if (!options)
return 0;
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
/*
* Initialize args struct so we know whether arg was
* found; some options take optional arguments.
*/
args[0].to = args[0].from = NULL;
token = match_token(p, f2fs_tokens, args);
switch (token) {
case Opt_gc_background:
name = match_strdup(&args[0]);
if (!name)
return -ENOMEM;
if (!strncmp(name, "on", 2))
set_opt(sbi, BG_GC);
else if (!strncmp(name, "off", 3))
clear_opt(sbi, BG_GC);
else {
kfree(name);
return -EINVAL;
}
kfree(name);
break;
case Opt_disable_roll_forward:
set_opt(sbi, DISABLE_ROLL_FORWARD);
break;
case Opt_discard:
set_opt(sbi, DISCARD);
break;
case Opt_noheap:
set_opt(sbi, NOHEAP);
break;
#ifdef CONFIG_F2FS_FS_XATTR
case Opt_nouser_xattr:
clear_opt(sbi, XATTR_USER);
break;
#else
case Opt_nouser_xattr:
f2fs_msg(sb, KERN_INFO,
"nouser_xattr options not supported");
break;
#endif
#ifdef CONFIG_F2FS_FS_POSIX_ACL
case Opt_noacl:
clear_opt(sbi, POSIX_ACL);
break;
#else
case Opt_noacl:
f2fs_msg(sb, KERN_INFO, "noacl options not supported");
break;
#endif
case Opt_active_logs:
if (args->from && match_int(args, &arg))
return -EINVAL;
if (arg != 2 && arg != 4 && arg != NR_CURSEG_TYPE)
return -EINVAL;
sbi->active_logs = arg;
break;
case Opt_disable_ext_identify:
set_opt(sbi, DISABLE_EXT_IDENTIFY);
break;
default:
f2fs_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" or missing value",
p);
return -EINVAL;
}
}
return 0;
}
static struct inode *f2fs_alloc_inode(struct super_block *sb) static struct inode *f2fs_alloc_inode(struct super_block *sb)
{ {
struct f2fs_inode_info *fi; struct f2fs_inode_info *fi;
...@@ -112,6 +197,17 @@ static int f2fs_drop_inode(struct inode *inode) ...@@ -112,6 +197,17 @@ static int f2fs_drop_inode(struct inode *inode)
return generic_drop_inode(inode); return generic_drop_inode(inode);
} }
/*
* f2fs_dirty_inode() is called from __mark_inode_dirty()
*
* We should call set_dirty_inode to write the dirty inode through write_inode.
*/
static void f2fs_dirty_inode(struct inode *inode, int flags)
{
set_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
return;
}
static void f2fs_i_callback(struct rcu_head *head) static void f2fs_i_callback(struct rcu_head *head)
{ {
struct inode *inode = container_of(head, struct inode, i_rcu); struct inode *inode = container_of(head, struct inode, i_rcu);
...@@ -170,7 +266,7 @@ static int f2fs_freeze(struct super_block *sb) ...@@ -170,7 +266,7 @@ static int f2fs_freeze(struct super_block *sb)
{ {
int err; int err;
if (sb->s_flags & MS_RDONLY) if (f2fs_readonly(sb))
return 0; return 0;
err = f2fs_sync_fs(sb, 1); err = f2fs_sync_fs(sb, 1);
...@@ -214,10 +310,10 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) ...@@ -214,10 +310,10 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb);
if (test_opt(sbi, BG_GC)) if (!(root->d_sb->s_flags & MS_RDONLY) && test_opt(sbi, BG_GC))
seq_puts(seq, ",background_gc_on"); seq_printf(seq, ",background_gc=%s", "on");
else else
seq_puts(seq, ",background_gc_off"); seq_printf(seq, ",background_gc=%s", "off");
if (test_opt(sbi, DISABLE_ROLL_FORWARD)) if (test_opt(sbi, DISABLE_ROLL_FORWARD))
seq_puts(seq, ",disable_roll_forward"); seq_puts(seq, ",disable_roll_forward");
if (test_opt(sbi, DISCARD)) if (test_opt(sbi, DISCARD))
...@@ -244,11 +340,64 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) ...@@ -244,11 +340,64 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
return 0; return 0;
} }
static int f2fs_remount(struct super_block *sb, int *flags, char *data)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
struct f2fs_mount_info org_mount_opt;
int err, active_logs;
/*
* Save the old mount options in case we
* need to restore them.
*/
org_mount_opt = sbi->mount_opt;
active_logs = sbi->active_logs;
/* parse mount options */
err = parse_options(sb, data);
if (err)
goto restore_opts;
/*
* Previous and new state of filesystem is RO,
* so no point in checking GC conditions.
*/
if ((sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY))
goto skip;
/*
* We stop the GC thread if FS is mounted as RO
* or if background_gc = off is passed in mount
* option. Also sync the filesystem.
*/
if ((*flags & MS_RDONLY) || !test_opt(sbi, BG_GC)) {
if (sbi->gc_thread) {
stop_gc_thread(sbi);
f2fs_sync_fs(sb, 1);
}
} else if (test_opt(sbi, BG_GC) && !sbi->gc_thread) {
err = start_gc_thread(sbi);
if (err)
goto restore_opts;
}
skip:
/* Update the POSIXACL Flag */
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
(test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0);
return 0;
restore_opts:
sbi->mount_opt = org_mount_opt;
sbi->active_logs = active_logs;
return err;
}
static struct super_operations f2fs_sops = { static struct super_operations f2fs_sops = {
.alloc_inode = f2fs_alloc_inode, .alloc_inode = f2fs_alloc_inode,
.drop_inode = f2fs_drop_inode, .drop_inode = f2fs_drop_inode,
.destroy_inode = f2fs_destroy_inode, .destroy_inode = f2fs_destroy_inode,
.write_inode = f2fs_write_inode, .write_inode = f2fs_write_inode,
.dirty_inode = f2fs_dirty_inode,
.show_options = f2fs_show_options, .show_options = f2fs_show_options,
.evict_inode = f2fs_evict_inode, .evict_inode = f2fs_evict_inode,
.put_super = f2fs_put_super, .put_super = f2fs_put_super,
...@@ -256,6 +405,7 @@ static struct super_operations f2fs_sops = { ...@@ -256,6 +405,7 @@ static struct super_operations f2fs_sops = {
.freeze_fs = f2fs_freeze, .freeze_fs = f2fs_freeze,
.unfreeze_fs = f2fs_unfreeze, .unfreeze_fs = f2fs_unfreeze,
.statfs = f2fs_statfs, .statfs = f2fs_statfs,
.remount_fs = f2fs_remount,
}; };
static struct inode *f2fs_nfs_get_inode(struct super_block *sb, static struct inode *f2fs_nfs_get_inode(struct super_block *sb,
...@@ -303,79 +453,6 @@ static const struct export_operations f2fs_export_ops = { ...@@ -303,79 +453,6 @@ static const struct export_operations f2fs_export_ops = {
.get_parent = f2fs_get_parent, .get_parent = f2fs_get_parent,
}; };
static int parse_options(struct super_block *sb, struct f2fs_sb_info *sbi,
char *options)
{
substring_t args[MAX_OPT_ARGS];
char *p;
int arg = 0;
if (!options)
return 0;
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
/*
* Initialize args struct so we know whether arg was
* found; some options take optional arguments.
*/
args[0].to = args[0].from = NULL;
token = match_token(p, f2fs_tokens, args);
switch (token) {
case Opt_gc_background_off:
clear_opt(sbi, BG_GC);
break;
case Opt_disable_roll_forward:
set_opt(sbi, DISABLE_ROLL_FORWARD);
break;
case Opt_discard:
set_opt(sbi, DISCARD);
break;
case Opt_noheap:
set_opt(sbi, NOHEAP);
break;
#ifdef CONFIG_F2FS_FS_XATTR
case Opt_nouser_xattr:
clear_opt(sbi, XATTR_USER);
break;
#else
case Opt_nouser_xattr:
f2fs_msg(sb, KERN_INFO,
"nouser_xattr options not supported");
break;
#endif
#ifdef CONFIG_F2FS_FS_POSIX_ACL
case Opt_noacl:
clear_opt(sbi, POSIX_ACL);
break;
#else
case Opt_noacl:
f2fs_msg(sb, KERN_INFO, "noacl options not supported");
break;
#endif
case Opt_active_logs:
if (args->from && match_int(args, &arg))
return -EINVAL;
if (arg != 2 && arg != 4 && arg != NR_CURSEG_TYPE)
return -EINVAL;
sbi->active_logs = arg;
break;
case Opt_disable_ext_identify:
set_opt(sbi, DISABLE_EXT_IDENTIFY);
break;
default:
f2fs_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" or missing value",
p);
return -EINVAL;
}
}
return 0;
}
static loff_t max_file_size(unsigned bits) static loff_t max_file_size(unsigned bits)
{ {
loff_t result = ADDRS_PER_INODE; loff_t result = ADDRS_PER_INODE;
...@@ -541,6 +618,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -541,6 +618,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
if (err) if (err)
goto free_sb_buf; goto free_sb_buf;
} }
sb->s_fs_info = sbi;
/* init some FS parameters */ /* init some FS parameters */
sbi->active_logs = NR_CURSEG_TYPE; sbi->active_logs = NR_CURSEG_TYPE;
...@@ -553,7 +631,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -553,7 +631,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
set_opt(sbi, POSIX_ACL); set_opt(sbi, POSIX_ACL);
#endif #endif
/* parse mount options */ /* parse mount options */
err = parse_options(sb, sbi, (char *)data); err = parse_options(sb, (char *)data);
if (err) if (err)
goto free_sb_buf; goto free_sb_buf;
...@@ -565,7 +643,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -565,7 +643,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_xattr = f2fs_xattr_handlers; sb->s_xattr = f2fs_xattr_handlers;
sb->s_export_op = &f2fs_export_ops; sb->s_export_op = &f2fs_export_ops;
sb->s_magic = F2FS_SUPER_MAGIC; sb->s_magic = F2FS_SUPER_MAGIC;
sb->s_fs_info = sbi;
sb->s_time_gran = 1; sb->s_time_gran = 1;
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
(test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0);
...@@ -674,10 +751,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -674,10 +751,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
"Cannot recover all fsync data errno=%ld", err); "Cannot recover all fsync data errno=%ld", err);
} }
/* After POR, we can run background GC thread */ /*
err = start_gc_thread(sbi); * If filesystem is not mounted as read-only then
if (err) * do start the gc_thread.
goto fail; */
if (!(sb->s_flags & MS_RDONLY)) {
/* After POR, we can run background GC thread.*/
err = start_gc_thread(sbi);
if (err)
goto fail;
}
err = f2fs_build_stats(sbi); err = f2fs_build_stats(sbi);
if (err) if (err)
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
*/ */
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/f2fs_fs.h> #include <linux/f2fs_fs.h>
#include <linux/security.h>
#include "f2fs.h" #include "f2fs.h"
#include "xattr.h" #include "xattr.h"
...@@ -43,6 +44,10 @@ static size_t f2fs_xattr_generic_list(struct dentry *dentry, char *list, ...@@ -43,6 +44,10 @@ static size_t f2fs_xattr_generic_list(struct dentry *dentry, char *list,
prefix = XATTR_TRUSTED_PREFIX; prefix = XATTR_TRUSTED_PREFIX;
prefix_len = XATTR_TRUSTED_PREFIX_LEN; prefix_len = XATTR_TRUSTED_PREFIX_LEN;
break; break;
case F2FS_XATTR_INDEX_SECURITY:
prefix = XATTR_SECURITY_PREFIX;
prefix_len = XATTR_SECURITY_PREFIX_LEN;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -50,7 +55,7 @@ static size_t f2fs_xattr_generic_list(struct dentry *dentry, char *list, ...@@ -50,7 +55,7 @@ static size_t f2fs_xattr_generic_list(struct dentry *dentry, char *list,
total_len = prefix_len + name_len + 1; total_len = prefix_len + name_len + 1;
if (list && total_len <= list_size) { if (list && total_len <= list_size) {
memcpy(list, prefix, prefix_len); memcpy(list, prefix, prefix_len);
memcpy(list+prefix_len, name, name_len); memcpy(list + prefix_len, name, name_len);
list[prefix_len + name_len] = '\0'; list[prefix_len + name_len] = '\0';
} }
return total_len; return total_len;
...@@ -70,13 +75,14 @@ static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name, ...@@ -70,13 +75,14 @@ static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name,
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
break; break;
case F2FS_XATTR_INDEX_SECURITY:
break;
default: default:
return -EINVAL; return -EINVAL;
} }
if (strcmp(name, "") == 0) if (strcmp(name, "") == 0)
return -EINVAL; return -EINVAL;
return f2fs_getxattr(dentry->d_inode, type, name, return f2fs_getxattr(dentry->d_inode, type, name, buffer, size);
buffer, size);
} }
static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name, static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name,
...@@ -93,13 +99,15 @@ static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name, ...@@ -93,13 +99,15 @@ static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name,
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
break; break;
case F2FS_XATTR_INDEX_SECURITY:
break;
default: default:
return -EINVAL; return -EINVAL;
} }
if (strcmp(name, "") == 0) if (strcmp(name, "") == 0)
return -EINVAL; return -EINVAL;
return f2fs_setxattr(dentry->d_inode, type, name, value, size); return f2fs_setxattr(dentry->d_inode, type, name, value, size, NULL);
} }
static size_t f2fs_xattr_advise_list(struct dentry *dentry, char *list, static size_t f2fs_xattr_advise_list(struct dentry *dentry, char *list,
...@@ -145,6 +153,31 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name, ...@@ -145,6 +153,31 @@ static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name,
return 0; return 0;
} }
#ifdef CONFIG_F2FS_FS_SECURITY
static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *page)
{
const struct xattr *xattr;
int err = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_SECURITY,
xattr->name, xattr->value,
xattr->value_len, (struct page *)page);
if (err < 0)
break;
}
return err;
}
int f2fs_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, struct page *ipage)
{
return security_inode_init_security(inode, dir, qstr,
&f2fs_initxattrs, ipage);
}
#endif
const struct xattr_handler f2fs_xattr_user_handler = { const struct xattr_handler f2fs_xattr_user_handler = {
.prefix = XATTR_USER_PREFIX, .prefix = XATTR_USER_PREFIX,
.flags = F2FS_XATTR_INDEX_USER, .flags = F2FS_XATTR_INDEX_USER,
...@@ -169,6 +202,14 @@ const struct xattr_handler f2fs_xattr_advise_handler = { ...@@ -169,6 +202,14 @@ const struct xattr_handler f2fs_xattr_advise_handler = {
.set = f2fs_xattr_advise_set, .set = f2fs_xattr_advise_set,
}; };
const struct xattr_handler f2fs_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
.flags = F2FS_XATTR_INDEX_SECURITY,
.list = f2fs_xattr_generic_list,
.get = f2fs_xattr_generic_get,
.set = f2fs_xattr_generic_set,
};
static const struct xattr_handler *f2fs_xattr_handler_map[] = { static const struct xattr_handler *f2fs_xattr_handler_map[] = {
[F2FS_XATTR_INDEX_USER] = &f2fs_xattr_user_handler, [F2FS_XATTR_INDEX_USER] = &f2fs_xattr_user_handler,
#ifdef CONFIG_F2FS_FS_POSIX_ACL #ifdef CONFIG_F2FS_FS_POSIX_ACL
...@@ -176,6 +217,9 @@ static const struct xattr_handler *f2fs_xattr_handler_map[] = { ...@@ -176,6 +217,9 @@ static const struct xattr_handler *f2fs_xattr_handler_map[] = {
[F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &f2fs_xattr_acl_default_handler, [F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &f2fs_xattr_acl_default_handler,
#endif #endif
[F2FS_XATTR_INDEX_TRUSTED] = &f2fs_xattr_trusted_handler, [F2FS_XATTR_INDEX_TRUSTED] = &f2fs_xattr_trusted_handler,
#ifdef CONFIG_F2FS_FS_SECURITY
[F2FS_XATTR_INDEX_SECURITY] = &f2fs_xattr_security_handler,
#endif
[F2FS_XATTR_INDEX_ADVISE] = &f2fs_xattr_advise_handler, [F2FS_XATTR_INDEX_ADVISE] = &f2fs_xattr_advise_handler,
}; };
...@@ -186,6 +230,9 @@ const struct xattr_handler *f2fs_xattr_handlers[] = { ...@@ -186,6 +230,9 @@ const struct xattr_handler *f2fs_xattr_handlers[] = {
&f2fs_xattr_acl_default_handler, &f2fs_xattr_acl_default_handler,
#endif #endif
&f2fs_xattr_trusted_handler, &f2fs_xattr_trusted_handler,
#ifdef CONFIG_F2FS_FS_SECURITY
&f2fs_xattr_security_handler,
#endif
&f2fs_xattr_advise_handler, &f2fs_xattr_advise_handler,
NULL, NULL,
}; };
...@@ -218,6 +265,8 @@ int f2fs_getxattr(struct inode *inode, int name_index, const char *name, ...@@ -218,6 +265,8 @@ int f2fs_getxattr(struct inode *inode, int name_index, const char *name,
return -ENODATA; return -ENODATA;
page = get_node_page(sbi, fi->i_xattr_nid); page = get_node_page(sbi, fi->i_xattr_nid);
if (IS_ERR(page))
return PTR_ERR(page);
base_addr = page_address(page); base_addr = page_address(page);
list_for_each_xattr(entry, base_addr) { list_for_each_xattr(entry, base_addr) {
...@@ -268,6 +317,8 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) ...@@ -268,6 +317,8 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
return 0; return 0;
page = get_node_page(sbi, fi->i_xattr_nid); page = get_node_page(sbi, fi->i_xattr_nid);
if (IS_ERR(page))
return PTR_ERR(page);
base_addr = page_address(page); base_addr = page_address(page);
list_for_each_xattr(entry, base_addr) { list_for_each_xattr(entry, base_addr) {
...@@ -296,7 +347,7 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) ...@@ -296,7 +347,7 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
} }
int f2fs_setxattr(struct inode *inode, int name_index, const char *name, int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
const void *value, size_t value_len) const void *value, size_t value_len, struct page *ipage)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
...@@ -335,7 +386,7 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, ...@@ -335,7 +386,7 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
set_new_dnode(&dn, inode, NULL, NULL, fi->i_xattr_nid); set_new_dnode(&dn, inode, NULL, NULL, fi->i_xattr_nid);
mark_inode_dirty(inode); mark_inode_dirty(inode);
page = new_node_page(&dn, XATTR_NODE_OFFSET); page = new_node_page(&dn, XATTR_NODE_OFFSET, ipage);
if (IS_ERR(page)) { if (IS_ERR(page)) {
alloc_nid_failed(sbi, fi->i_xattr_nid); alloc_nid_failed(sbi, fi->i_xattr_nid);
fi->i_xattr_nid = 0; fi->i_xattr_nid = 0;
...@@ -435,7 +486,10 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, ...@@ -435,7 +486,10 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name,
inode->i_ctime = CURRENT_TIME; inode->i_ctime = CURRENT_TIME;
clear_inode_flag(fi, FI_ACL_MODE); clear_inode_flag(fi, FI_ACL_MODE);
} }
update_inode_page(inode); if (ipage)
update_inode(inode, ipage);
else
update_inode_page(inode);
mutex_unlock_op(sbi, ilock); mutex_unlock_op(sbi, ilock);
return 0; return 0;
......
...@@ -112,21 +112,19 @@ extern const struct xattr_handler f2fs_xattr_trusted_handler; ...@@ -112,21 +112,19 @@ extern const struct xattr_handler f2fs_xattr_trusted_handler;
extern const struct xattr_handler f2fs_xattr_acl_access_handler; extern const struct xattr_handler f2fs_xattr_acl_access_handler;
extern const struct xattr_handler f2fs_xattr_acl_default_handler; extern const struct xattr_handler f2fs_xattr_acl_default_handler;
extern const struct xattr_handler f2fs_xattr_advise_handler; extern const struct xattr_handler f2fs_xattr_advise_handler;
extern const struct xattr_handler f2fs_xattr_security_handler;
extern const struct xattr_handler *f2fs_xattr_handlers[]; extern const struct xattr_handler *f2fs_xattr_handlers[];
extern int f2fs_setxattr(struct inode *inode, int name_index, const char *name, extern int f2fs_setxattr(struct inode *, int, const char *,
const void *value, size_t value_len); const void *, size_t, struct page *);
extern int f2fs_getxattr(struct inode *inode, int name_index, const char *name, extern int f2fs_getxattr(struct inode *, int, const char *, void *, size_t);
void *buffer, size_t buffer_size); extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t);
extern ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer,
size_t buffer_size);
#else #else
#define f2fs_xattr_handlers NULL #define f2fs_xattr_handlers NULL
static inline int f2fs_setxattr(struct inode *inode, int name_index, static inline int f2fs_setxattr(struct inode *inode, int name_index,
const char *name, const void *value, size_t value_len) const char *name, const void *value, size_t value_len)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -142,4 +140,14 @@ static inline ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, ...@@ -142,4 +140,14 @@ static inline ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer,
} }
#endif #endif
#ifdef CONFIG_F2FS_FS_SECURITY
extern int f2fs_init_security(struct inode *, struct inode *,
const struct qstr *, struct page *);
#else
static inline int f2fs_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr, struct page *ipage)
{
return 0;
}
#endif
#endif /* __F2FS_XATTR_H__ */ #endif /* __F2FS_XATTR_H__ */
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
#define F2FS_BLKSIZE 4096 /* support only 4KB block */ #define F2FS_BLKSIZE 4096 /* support only 4KB block */
#define F2FS_MAX_EXTENSION 64 /* # of extension entries */ #define F2FS_MAX_EXTENSION 64 /* # of extension entries */
#define NULL_ADDR 0x0U #define NULL_ADDR ((block_t)0) /* used as block_t addresses */
#define NEW_ADDR -1U #define NEW_ADDR ((block_t)-1) /* used as block_t addresses */
#define F2FS_ROOT_INO(sbi) (sbi->root_ino_num) #define F2FS_ROOT_INO(sbi) (sbi->root_ino_num)
#define F2FS_NODE_INO(sbi) (sbi->node_ino_num) #define F2FS_NODE_INO(sbi) (sbi->node_ino_num)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment