From 53dc9a67769d0a9733adb5156adfc07edcbc1ea3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 25 Jan 2013 16:01:21 -0500 Subject: f2fs: init_dent_inode() should take qstr for one thing, it doesn't (and shouldn't) use anything else from dentry; for another, on some call chains the dentry is fake and should be eliminated completely. Signed-off-by: Al Viro --- fs/f2fs/dir.c | 10 +++++----- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 951ed52748f6..51332291b4bd 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -265,7 +265,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, mutex_unlock_op(sbi, DENTRY_OPS); } -void init_dent_inode(struct dentry *dentry, struct page *ipage) +void init_dent_inode(const struct qstr *name, struct page *ipage) { struct f2fs_node *rn; @@ -274,10 +274,10 @@ void init_dent_inode(struct dentry *dentry, struct page *ipage) wait_on_page_writeback(ipage); - /* copy dentry info. to this inode page */ + /* copy name info. to this inode page */ rn = (struct f2fs_node *)page_address(ipage); - rn->i.i_namelen = cpu_to_le32(dentry->d_name.len); - memcpy(rn->i.i_name, dentry->d_name.name, dentry->d_name.len); + rn->i.i_namelen = cpu_to_le32(name->len); + memcpy(rn->i.i_name, name->name, name->len); set_page_dirty(ipage); } @@ -310,7 +310,7 @@ static int init_inode_metadata(struct inode *inode, struct dentry *dentry) if (IS_ERR(ipage)) return PTR_ERR(ipage); set_cold_node(inode, ipage); - init_dent_inode(dentry, ipage); + init_dent_inode(&dentry->d_name, ipage); f2fs_put_page(ipage, 1); } if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 13c6dfbb7183..732862596311 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -867,7 +867,7 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); ino_t f2fs_inode_by_name(struct inode *, struct qstr *); void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, struct page *, struct inode *); -void init_dent_inode(struct dentry *, struct page *); +void init_dent_inode(const struct qstr *, struct page *); int f2fs_add_link(struct dentry *, struct inode *); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *); int f2fs_make_empty(struct inode *, struct inode *); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5066bfd256c9..5caa94676f60 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -790,7 +790,7 @@ int new_inode_page(struct inode *inode, struct dentry *dentry) set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); mutex_lock_op(sbi, NODE_NEW); page = new_node_page(&dn, 0); - init_dent_inode(dentry, page); + init_dent_inode(&dentry->d_name, page); mutex_unlock_op(sbi, NODE_NEW); if (IS_ERR(page)) return PTR_ERR(page); -- cgit v1.2.3 From c004363dd6aa89f1ccbebd694f261f86db0c840a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 25 Jan 2013 16:04:58 -0500 Subject: f2fs: switch new_inode_page() from dentry to qstr Signed-off-by: Al Viro --- fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 51332291b4bd..ca7948a2770d 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -287,7 +287,7 @@ static int init_inode_metadata(struct inode *inode, struct dentry *dentry) if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { int err; - err = new_inode_page(inode, dentry); + err = new_inode_page(inode, &dentry->d_name); if (err) return err; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 732862596311..bfdc10741ff1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -894,7 +894,7 @@ 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 truncate_inode_blocks(struct inode *, pgoff_t); int remove_inode_page(struct inode *); -int new_inode_page(struct inode *, struct dentry *); +int new_inode_page(struct inode *, const struct qstr *); struct page *new_node_page(struct dnode_of_data *, unsigned int); void ra_node_page(struct f2fs_sb_info *, nid_t); struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5caa94676f60..6625ca819716 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -780,7 +780,7 @@ int remove_inode_page(struct inode *inode) return 0; } -int new_inode_page(struct inode *inode, struct dentry *dentry) +int new_inode_page(struct inode *inode, const struct qstr *name) { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct page *page; @@ -790,7 +790,7 @@ int new_inode_page(struct inode *inode, struct dentry *dentry) set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); mutex_lock_op(sbi, NODE_NEW); page = new_node_page(&dn, 0); - init_dent_inode(&dentry->d_name, page); + init_dent_inode(name, page); mutex_unlock_op(sbi, NODE_NEW); if (IS_ERR(page)) return PTR_ERR(page); -- cgit v1.2.3 From 69f24eac55725859a89c440ee2d19f36fa09e8fc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 25 Jan 2013 16:08:53 -0500 Subject: f2fs: switch init_inode_metadata() to passing parent and name separately ... sure, it's tempting to just pass dentry. Except that we don't _have_ anything resembling a real dentry on one of the paths to it. Signed-off-by: Al Viro --- fs/f2fs/dir.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index ca7948a2770d..208a804180d6 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -281,13 +281,12 @@ void init_dent_inode(const struct qstr *name, struct page *ipage) set_page_dirty(ipage); } -static int init_inode_metadata(struct inode *inode, struct dentry *dentry) +static int init_inode_metadata(struct inode *inode, + struct inode *dir, const struct qstr *name) { - struct inode *dir = dentry->d_parent->d_inode; - if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { int err; - err = new_inode_page(inode, &dentry->d_name); + err = new_inode_page(inode, name); if (err) return err; @@ -310,7 +309,7 @@ static int init_inode_metadata(struct inode *inode, struct dentry *dentry) if (IS_ERR(ipage)) return PTR_ERR(ipage); set_cold_node(inode, ipage); - init_dent_inode(&dentry->d_name, ipage); + init_dent_inode(name, ipage); f2fs_put_page(ipage, 1); } if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { @@ -433,7 +432,7 @@ start: ++level; goto start; add_dentry: - err = init_inode_metadata(inode, dentry); + err = init_inode_metadata(inode, dir, &dentry->d_name); if (err) goto fail; -- cgit v1.2.3 From b7f7a5e0be94d13875a1c6c9aa65eeb11a46fc1b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 25 Jan 2013 16:15:43 -0500 Subject: f2fs: get rid of fake on-stack dentries those should never be used for a lot of reasons... Signed-off-by: Al Viro --- fs/f2fs/dir.c | 12 +++++------- fs/f2fs/f2fs.h | 8 +++++++- fs/f2fs/recovery.c | 12 +++++------- 3 files changed, 17 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 208a804180d6..47df9252ad6a 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -370,7 +370,7 @@ next: goto next; } -int f2fs_add_link(struct dentry *dentry, struct inode *inode) +int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *inode) { unsigned int bit_pos; unsigned int level; @@ -379,17 +379,15 @@ int f2fs_add_link(struct dentry *dentry, struct inode *inode) f2fs_hash_t dentry_hash; struct f2fs_dir_entry *de; unsigned int nbucket, nblock; - struct inode *dir = dentry->d_parent->d_inode; struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - const char *name = dentry->d_name.name; - size_t namelen = dentry->d_name.len; + size_t namelen = name->len; struct page *dentry_page = NULL; struct f2fs_dentry_block *dentry_blk = NULL; int slots = GET_DENTRY_SLOTS(namelen); int err = 0; int i; - dentry_hash = f2fs_dentry_hash(name, dentry->d_name.len); + dentry_hash = f2fs_dentry_hash(name->name, name->len); level = 0; current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { @@ -432,7 +430,7 @@ start: ++level; goto start; add_dentry: - err = init_inode_metadata(inode, dir, &dentry->d_name); + err = init_inode_metadata(inode, dir, name); if (err) goto fail; @@ -441,7 +439,7 @@ add_dentry: de = &dentry_blk->dentry[bit_pos]; de->hash_code = dentry_hash; de->name_len = cpu_to_le16(namelen); - memcpy(dentry_blk->filename[bit_pos], name, namelen); + memcpy(dentry_blk->filename[bit_pos], name->name, name->len); de->ino = cpu_to_le32(inode->i_ino); set_de_type(de, inode); for (i = 0; i < slots; i++) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index bfdc10741ff1..6895ecc351ed 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -868,11 +868,17 @@ ino_t f2fs_inode_by_name(struct inode *, struct qstr *); void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, struct page *, struct inode *); void init_dent_inode(const struct qstr *, struct page *); -int f2fs_add_link(struct dentry *, 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 *); int f2fs_make_empty(struct inode *, struct inode *); bool f2fs_empty_dir(struct inode *); +static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) +{ + return __f2fs_add_link(dentry->d_parent->d_inode, &dentry->d_name, + inode); +} + /* * super.c */ diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index b571fee677d5..62000422879a 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -42,7 +42,7 @@ static int recover_dentry(struct page *ipage, struct inode *inode) { struct f2fs_node *raw_node = (struct f2fs_node *)kmap(ipage); struct f2fs_inode *raw_inode = &(raw_node->i); - struct dentry dent, parent; + struct qstr name; struct f2fs_dir_entry *de; struct page *page; struct inode *dir; @@ -57,17 +57,15 @@ static int recover_dentry(struct page *ipage, struct inode *inode) goto out; } - parent.d_inode = dir; - dent.d_parent = &parent; - dent.d_name.len = le32_to_cpu(raw_inode->i_namelen); - dent.d_name.name = raw_inode->i_name; + name.len = le32_to_cpu(raw_inode->i_namelen); + name.name = raw_inode->i_name; - de = f2fs_find_entry(dir, &dent.d_name, &page); + de = f2fs_find_entry(dir, &name, &page); if (de) { kunmap(page); f2fs_put_page(page, 0); } else { - f2fs_add_link(&dent, inode); + __f2fs_add_link(dir, &name, inode); } iput(dir); out: -- cgit v1.2.3 From 7d79e75f6420fc13cfe72554b3ea5afad24c8625 Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Wed, 23 Jan 2013 09:40:23 +0900 Subject: f2fs: save device node number into f2fs_inode This patch stores inode->i_rdev into on-disk inode structure. Alun reported that: aspire tmp # mount -t f2fs /dev/sdb mnt aspire tmp # mknod mnt/sda1 b 8 1 aspire tmp # mknod mnt/null c 1 3 aspire tmp # mknod mnt/console c 5 1 aspire tmp # ls -l mnt total 2 crw-r--r-- 1 root root 5, 1 Jan 22 18:44 console crw-r--r-- 1 root root 1, 3 Jan 22 18:44 null brw-r--r-- 1 root root 8, 1 Jan 22 18:44 sda1 aspire tmp # umount mnt aspire tmp # mount -t f2fs /dev/sdb mnt aspire tmp # ls -l mnt total 2 crw-r--r-- 1 root root 0, 0 Jan 22 18:44 console crw-r--r-- 1 root root 0, 0 Jan 22 18:44 null brw-r--r-- 1 root root 0, 0 Jan 22 18:44 sda1 In this report, f2fs lost the major/minor numbers of device files after umount. The reason was revealed that f2fs does not store the inode->i_rdev to the on-disk inode data structure. So, as the other file systems do, f2fs also stores i_rdev into the i_addr fields in on-disk inode structure without any on-disk layout changes. Note that, this bug is limited to device files made by mknod(). Reported-and-Tested-by: Alun Jones Signed-off-by: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'fs') diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 794241777322..340a21be5a76 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -100,6 +100,10 @@ static int do_read_inode(struct inode *inode) inode->i_ctime.tv_nsec = le32_to_cpu(ri->i_ctime_nsec); inode->i_mtime.tv_nsec = le32_to_cpu(ri->i_mtime_nsec); inode->i_generation = le32_to_cpu(ri->i_generation); + if (ri->i_addr[0]) + inode->i_rdev = old_decode_dev(le32_to_cpu(ri->i_addr[0])); + else + inode->i_rdev = new_decode_dev(le32_to_cpu(ri->i_addr[1])); fi->i_current_depth = le32_to_cpu(ri->i_current_depth); fi->i_xattr_nid = le32_to_cpu(ri->i_xattr_nid); @@ -203,6 +207,20 @@ void update_inode(struct inode *inode, struct page *node_page) ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags); ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino); ri->i_generation = cpu_to_le32(inode->i_generation); + + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { + if (old_valid_dev(inode->i_rdev)) { + ri->i_addr[0] = + cpu_to_le32(old_encode_dev(inode->i_rdev)); + ri->i_addr[1] = 0; + } else { + ri->i_addr[0] = 0; + ri->i_addr[1] = + cpu_to_le32(new_encode_dev(inode->i_rdev)); + ri->i_addr[2] = 0; + } + } + set_cold_node(inode, node_page); set_page_dirty(node_page); } -- cgit v1.2.3 From 577e349514452fa3fcd99fd06e587b02d3d1cf28 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 24 Jan 2013 19:56:11 +0900 Subject: f2fs: prevent checkpoint once any IO failure is detected This patch enhances the checkpoint routine to cope with IO errors. Basically f2fs detects IO errors from end_io_write, and the errors are able to be occurred during one of data, node, and meta page writes. In the previous code, when an IO error is occurred during writes, f2fs sets a flag, CP_ERROR_FLAG, in the raw ckeckpoint buffer which will be written to disk. Afterwards, write_checkpoint() will check the flag and remount f2fs as a read-only (ro) mode. However, even once f2fs is remounted as a ro mode, dirty checkpoint pages are freely able to be written to disk by flusher or kswapd in background. In such a case, after cold reboot, f2fs would restore the checkpoint data having CP_ERROR_FLAG, resulting in disabling write_checkpoint and remounting f2fs as a ro mode again. Therefore, let's prevent any checkpoint page (meta) writes once an IO error is occurred, and remount f2fs as a ro mode right away at that moment. Reported-by: Oliver Winker Signed-off-by: Jaegeuk Kim Reviewed-by: Namjae Jeon --- fs/f2fs/checkpoint.c | 36 +++++++++++++++++++----------------- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/segment.c | 8 ++------ fs/f2fs/super.c | 12 +++++++++--- 4 files changed, 31 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index ff3c8439af87..9c1627165039 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -72,22 +72,22 @@ static int f2fs_write_meta_page(struct page *page, { struct inode *inode = page->mapping->host; struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - int err; - wait_on_page_writeback(page); - - err = write_meta_page(sbi, page, wbc); - if (err) { + /* Should not write any meta pages, if any IO error was occurred */ + if (wbc->for_reclaim || + is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ERROR_FLAG)) { + dec_page_count(sbi, F2FS_DIRTY_META); wbc->pages_skipped++; set_page_dirty(page); + return AOP_WRITEPAGE_ACTIVATE; } - dec_page_count(sbi, F2FS_DIRTY_META); + wait_on_page_writeback(page); - /* In this case, we should not unlock this page */ - if (err != AOP_WRITEPAGE_ACTIVATE) - unlock_page(page); - return err; + write_meta_page(sbi, page); + dec_page_count(sbi, F2FS_DIRTY_META); + unlock_page(page); + return 0; } static int f2fs_write_meta_pages(struct address_space *mapping, @@ -138,7 +138,10 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, BUG_ON(page->mapping != mapping); BUG_ON(!PageDirty(page)); clear_page_dirty_for_io(page); - f2fs_write_meta_page(page, &wbc); + if (f2fs_write_meta_page(page, &wbc)) { + unlock_page(page); + break; + } if (nwritten++ >= nr_to_write) break; } @@ -717,13 +720,12 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) sbi->alloc_valid_block_count = 0; /* Here, we only have one bio having CP pack */ - if (is_set_ckpt_flags(ckpt, CP_ERROR_FLAG)) - sbi->sb->s_flags |= MS_RDONLY; - else - sync_meta_pages(sbi, META_FLUSH, LONG_MAX); + sync_meta_pages(sbi, META_FLUSH, LONG_MAX); - clear_prefree_segments(sbi); - F2FS_RESET_SB_DIRT(sbi); + if (!is_set_ckpt_flags(ckpt, CP_ERROR_FLAG)) { + clear_prefree_segments(sbi); + F2FS_RESET_SB_DIRT(sbi); + } } /* diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c8e2d751ef9c..f4f509766465 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -929,8 +929,7 @@ void allocate_new_segments(struct f2fs_sb_info *); struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); struct bio *f2fs_bio_alloc(struct block_device *, int); void f2fs_submit_bio(struct f2fs_sb_info *, enum page_type, bool sync); -int write_meta_page(struct f2fs_sb_info *, struct page *, - struct writeback_control *); +void write_meta_page(struct f2fs_sb_info *, struct page *); void write_node_page(struct f2fs_sb_info *, struct page *, unsigned int, block_t, block_t *); void write_data_page(struct inode *, struct page *, struct dnode_of_data*, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4b0099066582..7aa270f3538a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -600,6 +600,7 @@ static void f2fs_end_io_write(struct bio *bio, int err) if (page->mapping) set_bit(AS_EIO, &page->mapping->flags); set_ckpt_flags(p->sbi->ckpt, CP_ERROR_FLAG); + p->sbi->sb->s_flags |= MS_RDONLY; } end_page_writeback(page); dec_page_count(p->sbi, F2FS_WRITEBACK); @@ -815,15 +816,10 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, mutex_unlock(&curseg->curseg_mutex); } -int write_meta_page(struct f2fs_sb_info *sbi, struct page *page, - struct writeback_control *wbc) +void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) { - if (wbc->for_reclaim) - return AOP_WRITEPAGE_ACTIVATE; - set_page_writeback(page); submit_write_page(sbi, page, page->index, META); - return 0; } void write_node_page(struct f2fs_sb_info *sbi, struct page *page, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 37fad04c8669..117ca2a46e39 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -387,10 +387,11 @@ static int sanity_check_raw_super(struct super_block *sb, return 0; } -static int sanity_check_ckpt(struct f2fs_super_block *raw_super, - struct f2fs_checkpoint *ckpt) +static int sanity_check_ckpt(struct f2fs_sb_info *sbi) { unsigned int total, fsmeta; + struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); total = le32_to_cpu(raw_super->segment_count); fsmeta = le32_to_cpu(raw_super->segment_count_ckpt); @@ -401,6 +402,11 @@ static int sanity_check_ckpt(struct f2fs_super_block *raw_super, if (fsmeta >= total) return 1; + + if (is_set_ckpt_flags(ckpt, CP_ERROR_FLAG)) { + f2fs_msg(sbi->sb, KERN_ERR, "A bug case: need to run fsck"); + return 1; + } return 0; } @@ -525,7 +531,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) /* sanity checking of checkpoint */ err = -EINVAL; - if (sanity_check_ckpt(raw_super, sbi->ckpt)) { + if (sanity_check_ckpt(sbi)) { f2fs_msg(sb, KERN_ERR, "Invalid F2FS checkpoint"); goto free_cp; } -- cgit v1.2.3 From bd43df021ac37247f2db58ff376fb4032170f754 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 25 Jan 2013 18:33:41 +0900 Subject: f2fs: cover global locks for reserve_new_block The fill_zero() from fallocate() calls get_new_data_page() in which calls reserve_new_block(). The reserve_new_block() should be covered by *DATA_NEW*, one of global locks. And also, before getting the lock, we should check free sections by calling f2fs_balance_fs(). If we break this rule, f2fs is able to face with out-of-control free space management and fall into infinite loop like the following scenario as well. [f2fs_sync_fs()] [fallocate()] - write_checkpoint() - fill_zero() - block_operations() - get_new_data_page() : grab NODE_NEW - get_dnode_of_data() : get locked dirty node page - sync_node_pages() : try to grab NODE_NEW for data allocation : trylock and skip the dirty node page : call sync_node_pages() repeatedly in order to flush all the dirty node pages! In order to avoid this, we should grab another global lock such as DATA_NEW before calling get_new_data_page() in fill_zero(). Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs') diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3191b52aafb0..6cdab2c64fc6 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -387,12 +387,17 @@ const struct inode_operations f2fs_file_inode_operations = { static void fill_zero(struct inode *inode, pgoff_t index, loff_t start, loff_t len) { + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct page *page; if (!len) return; + f2fs_balance_fs(sbi); + + mutex_lock_op(sbi, DATA_NEW); page = get_new_data_page(inode, index, false); + mutex_unlock_op(sbi, DATA_NEW); if (!IS_ERR(page)) { wait_on_page_writeback(page); -- cgit v1.2.3 From aa43507f68e44fbc014d820e47eda5a369b8dc9d Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Fri, 25 Jan 2013 19:08:59 +0100 Subject: f2fs: fix disable_ext_identify option spelling There is a typo in the ->show_options function for disable_ext_identify. Fix it to match the spelling from the documentation. Signed-off-by: Alejandro Martinez Ruiz Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 117ca2a46e39..25656b6869a4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -198,7 +198,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",noacl"); #endif if (test_opt(sbi, DISABLE_EXT_IDENTIFY)) - seq_puts(seq, ",disable_ext_indentify"); + seq_puts(seq, ",disable_ext_identify"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); -- cgit v1.2.3 From a2617dc6863b21a8109c199ab533b3dbfe178f27 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Tue, 29 Jan 2013 16:19:02 +0800 Subject: f2fs: clean up the add_orphan_inode func For the code > prev = list_entry(orphan->list.prev, typeof(*prev), list); if orphan->list.prev == head, it can't get the right prev. And we can use the parameter 'this' to add. Signed-off-by: Jianpeng Ma Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 9c1627165039..d3b34d05211f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -219,19 +219,11 @@ retry: new->ino = ino; /* add new_oentry into list which is sorted by inode number */ - if (orphan) { - struct orphan_inode_entry *prev; - - /* get previous entry */ - prev = list_entry(orphan->list.prev, typeof(*prev), list); - if (&prev->list != head) - /* insert new orphan inode entry */ - list_add(&new->list, &prev->list); - else - list_add(&new->list, head); - } else { + if (orphan) + list_add(&new->list, this->prev); + else list_add_tail(&new->list, head); - } + sbi->n_orphans++; out: mutex_unlock(&sbi->orphan_inode_mutex); -- cgit v1.2.3 From d6212a5f18c8f9f9cc884070a96e11907711217f Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Tue, 29 Jan 2013 18:30:07 +0900 Subject: f2fs: add un/freeze_fs into super_operations This patch supports ioctl FIFREEZE and FITHAW to snapshot filesystem. Before calling f2fs_freeze, all writers would be suspended and sync_fs would be completed. So no f2fs has to do something. Just background gc operation should be skipped due to generate dirty nodes and data until unfreeze. Signed-off-by: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 5 +++++ fs/f2fs/inode.c | 2 ++ fs/f2fs/super.c | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+) (limited to 'fs') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c386910dacc5..9b5d0aad5daf 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -44,6 +44,11 @@ static int gc_thread_func(void *data) if (kthread_should_stop()) break; + if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { + wait_ms = GC_THREAD_MAX_SLEEP_TIME; + continue; + } + f2fs_balance_fs(sbi); if (!test_opt(sbi, BG_GC)) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 340a21be5a76..62433c63e2db 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -278,6 +278,7 @@ void f2fs_evict_inode(struct inode *inode) if (inode->i_nlink || is_bad_inode(inode)) goto no_delete; + sb_start_intwrite(inode->i_sb); set_inode_flag(F2FS_I(inode), FI_NO_ALLOC); i_size_write(inode, 0); @@ -285,6 +286,7 @@ void f2fs_evict_inode(struct inode *inode) f2fs_truncate(inode); remove_inode_page(inode); + sb_end_intwrite(inode->i_sb); no_delete: clear_inode(inode); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 25656b6869a4..0b18aee2ed25 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -143,6 +143,22 @@ int f2fs_sync_fs(struct super_block *sb, int sync) return 0; } +static int f2fs_freeze(struct super_block *sb) +{ + int err; + + if (sb->s_flags & MS_RDONLY) + return 0; + + err = f2fs_sync_fs(sb, 1); + return err; +} + +static int f2fs_unfreeze(struct super_block *sb) +{ + return 0; +} + static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; @@ -213,6 +229,8 @@ static struct super_operations f2fs_sops = { .evict_inode = f2fs_evict_inode, .put_super = f2fs_put_super, .sync_fs = f2fs_sync_fs, + .freeze_fs = f2fs_freeze, + .unfreeze_fs = f2fs_unfreeze, .statfs = f2fs_statfs, }; -- cgit v1.2.3 From 3786dfdf4f00755ce5797c0e87eac8f898acb52c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jan 2013 22:47:02 +0900 Subject: f2fs: avoid redundant call to has_not_enough_free_secs in f2fs_gc After doing a write_checkpoint from garbage collection path if there is still need to do more garbage collection, gc_more label is used to jump and start the process again. And in that process, first step before getting victim is to check if there are not enough free sections, which is already done before doing a jump to gc_more. We can avoid the redundant call to check free sections, by checking the gc_type flag which will remain FG_GC(value 1) under this condition. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 9b5d0aad5daf..fb03be68cb20 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -672,7 +672,7 @@ gc_more: if (!(sbi->sb->s_flags & MS_ACTIVE)) goto stop; - if (has_not_enough_free_secs(sbi)) + if (gc_type == BG_GC && has_not_enough_free_secs(sbi)) gc_type = FG_GC; if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) -- cgit v1.2.3 From a2b52a598a4df55782462acba3bdced1a4610a60 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jan 2013 22:47:16 +0900 Subject: f2fs: reorganize code for ra_node_page We can remove unneeded label unlock_out, avoid unnecessary jump and reorganize the returning conditions in this function. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9bda63c9c166..f71dfbbcb2b0 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -874,15 +874,11 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) return; if (read_node_page(apage, READA)) - goto unlock_out; + unlock_page(apage); - page_cache_release(apage); - return; - -unlock_out: - unlock_page(apage); release_out: page_cache_release(apage); + return; } struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) -- cgit v1.2.3 From 324ddc702eb89fb4ec8ed0cb38b4563ba4804f88 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jan 2013 22:47:29 +0900 Subject: f2fs: fix typo mistake for data_version description In f2fs_inode_info structure, the description for data_version has a typo mistake. It should be latest instead of lastes. So, correcting that. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f4f509766465..975cb44851ab 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -141,7 +141,7 @@ struct f2fs_inode_info { /* Use below internally in f2fs*/ unsigned long flags; /* use to pass per-file flags */ - unsigned long long data_version;/* lastes version of data for fsync */ + unsigned long long data_version;/* latest version of data for fsync */ atomic_t dirty_dents; /* # of dirty dentry pages */ f2fs_hash_t chash; /* hash value of given file name */ unsigned int clevel; /* maximum level of given file name */ -- cgit v1.2.3 From 369a708c2a6557fabd409cf17a03db271c54931a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Jan 2013 10:15:35 +0900 Subject: f2fs: remove the use of page_cache_release Let's remove the use of page_cache_release() in f2fs, and instead, use f2fs_put_page(page, 0) which is exactly same but for code readability. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index f71dfbbcb2b0..33fa6d506d94 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -104,7 +104,7 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid) f2fs_put_page(page, 1); continue; } - page_cache_release(page); + f2fs_put_page(page, 0); } } @@ -877,7 +877,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) unlock_page(apage); release_out: - page_cache_release(apage); + f2fs_put_page(apage, 0); return; } -- cgit v1.2.3 From d4686d56ec912d55fd8a9d6d509de50de24e90ab Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Jan 2013 15:36:04 +0900 Subject: f2fs: avoid balanc_fs during evict_inode 1. Background Previously, if f2fs tries to move data blocks of an *evicting* inode during the cleaning process, it stops the process incompletely and then restarts the whole process, since it needs a locked inode to grab victim data pages in its address space. In order to get a locked inode, iget_locked() by f2fs_iget() is normally used, but, it waits if the inode is on freeing. So, here is a deadlock scenario. 1. f2fs_evict_inode() <- inode "A" 2. f2fs_balance_fs() 3. f2fs_gc() 4. gc_data_segment() 5. f2fs_iget() <- inode "A" too! If step #1 and #5 treat a same inode "A", step #5 would fall into deadlock since the inode "A" is on freeing. In order to resolve this, f2fs_iget_nowait() which skips __wait_on_freeing_inode() was introduced in step #5, and stops f2fs_gc() to complete f2fs_evict_inode(). 1. f2fs_evict_inode() <- inode "A" 2. f2fs_balance_fs() 3. f2fs_gc() 4. gc_data_segment() 5. f2fs_iget_nowait() <- inode "A", then stop f2fs_gc() w/ -ENOENT 2. Problem and Solution In the above scenario, however, f2fs cannot finish f2fs_evict_inode() only if: o there are not enough free sections, and o f2fs_gc() tries to move data blocks of the *evicting* inode repeatedly. So, the final solution is to use f2fs_iget() and remove f2fs_balance_fs() in f2fs_evict_inode(). The f2fs_evict_inode() actually truncates all the data and node blocks, which means that it doesn't produce any dirty node pages accordingly. So, we don't need to do f2fs_balance_fs() in practical. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 - fs/f2fs/file.c | 3 +-- fs/f2fs/gc.c | 2 +- fs/f2fs/inode.c | 33 --------------------------------- fs/f2fs/recovery.c | 2 +- 5 files changed, 3 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 975cb44851ab..5022a7d7f7ca 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -847,7 +847,6 @@ long f2fs_ioctl(struct file *, unsigned int, unsigned long); * inode.c */ void f2fs_set_inode_flags(struct inode *); -struct inode *f2fs_iget_nowait(struct super_block *, unsigned long); struct inode *f2fs_iget(struct super_block *, unsigned long); void update_inode(struct inode *, struct page *); int f2fs_write_inode(struct inode *, struct writeback_control *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6cdab2c64fc6..3efa4739b00c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -298,8 +298,6 @@ void f2fs_truncate(struct inode *inode) inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); } - - f2fs_balance_fs(F2FS_SB(inode->i_sb)); } static int f2fs_getattr(struct vfsmount *mnt, @@ -356,6 +354,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) attr->ia_size != i_size_read(inode)) { truncate_setsize(inode, attr->ia_size); f2fs_truncate(inode); + f2fs_balance_fs(F2FS_SB(inode->i_sb)); } __setattr_copy(inode, attr); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index fb03be68cb20..375e69e2c6f1 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -579,7 +579,7 @@ next_step: ofs_in_node = le16_to_cpu(entry->ofs_in_node); if (phase == 2) { - inode = f2fs_iget_nowait(sb, dni.ino); + inode = f2fs_iget(sb, dni.ino); if (IS_ERR(inode)) continue; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 62433c63e2db..ddae412d30c8 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -16,11 +16,6 @@ #include "f2fs.h" #include "node.h" -struct f2fs_iget_args { - u64 ino; - int on_free; -}; - void f2fs_set_inode_flags(struct inode *inode) { unsigned int flags = F2FS_I(inode)->i_flags; @@ -40,34 +35,6 @@ void f2fs_set_inode_flags(struct inode *inode) inode->i_flags |= S_DIRSYNC; } -static int f2fs_iget_test(struct inode *inode, void *data) -{ - struct f2fs_iget_args *args = data; - - if (inode->i_ino != args->ino) - return 0; - if (inode->i_state & (I_FREEING | I_WILL_FREE)) { - args->on_free = 1; - return 0; - } - return 1; -} - -struct inode *f2fs_iget_nowait(struct super_block *sb, unsigned long ino) -{ - struct f2fs_iget_args args = { - .ino = ino, - .on_free = 0 - }; - struct inode *inode = ilookup5(sb, ino, f2fs_iget_test, &args); - - if (inode) - return inode; - if (!args.on_free) - return f2fs_iget(sb, ino); - return ERR_PTR(-ENOENT); -} - static int do_read_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index f42e4060b399..e2a3e1a8eae9 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -226,7 +226,7 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi, f2fs_put_page(node_page, 1); /* Deallocate previous index in the node page */ - inode = f2fs_iget_nowait(sbi->sb, ino); + inode = f2fs_iget(sbi->sb, ino); if (IS_ERR(inode)) return; -- cgit v1.2.3 From facb0205401a6ce4be410e1f4b9357bbc6caa36a Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Thu, 31 Jan 2013 17:16:56 +0900 Subject: f2fs: stop repeated checking if cp is needed If it is decided that f2fs should do checkpoint, skip next comparison. Signed-off-by: Changman Lee --- fs/f2fs/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3efa4739b00c..33d1736ee5f9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -157,11 +157,11 @@ 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) need_cp = true; - if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP)) + else if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP)) need_cp = true; - if (!space_for_roll_forward(sbi)) + else if (!space_for_roll_forward(sbi)) need_cp = true; - if (need_to_sync_dir(sbi, inode)) + else if (need_to_sync_dir(sbi, inode)) need_cp = true; if (need_cp) { -- cgit v1.2.3 From f83759e28372e593879f4dd20eb6c5ba6c4f393a Mon Sep 17 00:00:00 2001 From: majianpeng Date: Fri, 1 Feb 2013 15:00:30 +0800 Subject: f2fs: add device name in debugfs In file status, it can't distinguish between different devices. So add device name to do this function. Signed-off-by: Jianpeng Ma Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index c8c37307b326..025b9e2f935d 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -183,10 +183,12 @@ static int stat_show(struct seq_file *s, void *v) mutex_lock(&f2fs_stat_mutex); list_for_each_entry_safe(si, next, &f2fs_stat_list, stat_list) { + char devname[BDEVNAME_SIZE]; update_general_status(si->sbi); - seq_printf(s, "\n=====[ partition info. #%d ]=====\n", i++); + seq_printf(s, "\n=====[ partition info(%s). #%d ]=====\n", + bdevname(si->sbi->sb->s_bdev, devname), i++); seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ", si->sit_area_segs, si->nat_area_segs); seq_printf(s, "[SSA: %d] [MAIN: %d", -- cgit v1.2.3 From 5c9b469295fb6b10d98923eab5e79c4edb80ed20 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Fri, 1 Feb 2013 19:07:57 +0800 Subject: f2fs: use F2FS_BLKSIZE to judge bloksize and page_cache_size In some system PAGE_CACHE_SIZE isn't 4K. So using F2FS_BLKSIZE to judge. By Jaegeuk Kim: o f2fs does not support no other 4KB page cache size. Signed-off-by: Jianpeng Ma Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0b18aee2ed25..1d7fe11fea30 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -384,14 +384,23 @@ static int sanity_check_raw_super(struct super_block *sb, return 1; } + /* Currently, support only 4KB page cache size */ + if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) { + f2fs_msg(sb, KERN_INFO, + "Invalid page_cache_size (%u), supports only 4KB\n", + PAGE_CACHE_SIZE); + return 1; + } + /* Currently, support only 4KB block size */ blocksize = 1 << le32_to_cpu(raw_super->log_blocksize); - if (blocksize != PAGE_CACHE_SIZE) { + if (blocksize != F2FS_BLKSIZE) { f2fs_msg(sb, KERN_INFO, "Invalid blocksize (%u), supports only 4KB\n", blocksize); return 1; } + if (le32_to_cpu(raw_super->log_sectorsize) != F2FS_LOG_SECTOR_SIZE) { f2fs_msg(sb, KERN_INFO, "Invalid log sectorsize"); -- cgit v1.2.3 From 14d7e9de0500e9110e5820e2e77d096fc1bab724 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Fri, 1 Feb 2013 19:07:03 +0800 Subject: f2fs: when check superblock failed, try to check another superblock In f2fs, there are two superblocks. So when the first superblock was invalidate, it should try to check another. By Jaegeuk Kim: o Remove a white space for coding style o Clean up for code readability o Fix a typo Signed-off-by: Jianpeng Ma Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1d7fe11fea30..ddb665f54d17 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -387,7 +387,7 @@ static int sanity_check_raw_super(struct super_block *sb, /* Currently, support only 4KB page cache size */ if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) { f2fs_msg(sb, KERN_INFO, - "Invalid page_cache_size (%u), supports only 4KB\n", + "Invalid page_cache_size (%lu), supports only 4KB\n", PAGE_CACHE_SIZE); return 1; } @@ -462,6 +462,32 @@ static void init_sb_info(struct f2fs_sb_info *sbi) atomic_set(&sbi->nr_pages[i], 0); } +static int validate_superblock(struct super_block *sb, + struct f2fs_super_block **raw_super, + struct buffer_head **raw_super_buf, sector_t block) +{ + const char *super = (block == 0 ? "first" : "second"); + + /* read f2fs raw super block */ + *raw_super_buf = sb_bread(sb, block); + if (!*raw_super_buf) { + f2fs_msg(sb, KERN_ERR, "unable to read %s superblock", + super); + return 1; + } + + *raw_super = (struct f2fs_super_block *) + ((char *)(*raw_super_buf)->b_data + F2FS_SUPER_OFFSET); + + /* sanity checking of raw super */ + if (!sanity_check_raw_super(sb, *raw_super)) + return 0; + + f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem " + "in %s superblock", super); + return 1; +} + static int f2fs_fill_super(struct super_block *sb, void *data, int silent) { struct f2fs_sb_info *sbi; @@ -482,16 +508,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) goto free_sbi; } - /* read f2fs raw super block */ - raw_super_buf = sb_bread(sb, 0); - if (!raw_super_buf) { - err = -EIO; - f2fs_msg(sb, KERN_ERR, "unable to read superblock"); - goto free_sbi; + if (validate_superblock(sb, &raw_super, &raw_super_buf, 0)) { + brelse(raw_super_buf); + if (validate_superblock(sb, &raw_super, &raw_super_buf, 1)) + goto free_sb_buf; } - raw_super = (struct f2fs_super_block *) - ((char *)raw_super_buf->b_data + F2FS_SUPER_OFFSET); - /* init some FS parameters */ sbi->active_logs = NR_CURSEG_TYPE; @@ -507,12 +528,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) if (parse_options(sb, sbi, (char *)data)) goto free_sb_buf; - /* sanity checking of raw super */ - if (sanity_check_raw_super(sb, raw_super)) { - f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem"); - goto free_sb_buf; - } - sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize)); sb->s_max_links = F2FS_LINK_MAX; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); -- cgit v1.2.3 From 94787d91cba7ba168b028703b50a0224702ace9c Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Fri, 1 Feb 2013 18:05:09 +0900 Subject: f2fs: remove repeated F2FS_SET_SB_DIRT call F2FS_SET_SB_DIRT is called in inc_page_count and it is directly called one more time in the next line. Signed-off-by: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index d3b34d05211f..2887c196b0a2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -164,7 +164,6 @@ static int f2fs_set_meta_page_dirty(struct page *page) if (!PageDirty(page)) { __set_page_dirty_nobuffers(page); inc_page_count(sbi, F2FS_DIRTY_META); - F2FS_SET_SB_DIRT(sbi); return 1; } return 0; -- cgit v1.2.3 From 48600e44c18e4eb0f7c02ec8633c4c56aef292f0 Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Mon, 4 Feb 2013 10:05:09 +0900 Subject: f2fs: remove unnecessary gc option check and balance_fs 1. If f2fs is mounted with background_gc_off option, checking BG_GC is not redundant. 2. f2fs_balance_fs is checked in f2fs_gc, so this is also redundant. Signed-off-by: Changman Lee Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 375e69e2c6f1..8d293cb685ba 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -49,11 +49,6 @@ static int gc_thread_func(void *data) continue; } - f2fs_balance_fs(sbi); - - if (!test_opt(sbi, BG_GC)) - continue; - /* * [GC triggering condition] * 0. GC is not conducted currently. @@ -96,6 +91,8 @@ int start_gc_thread(struct f2fs_sb_info *sbi) { struct f2fs_gc_kthread *gc_th; + if (!test_opt(sbi, BG_GC)) + return 0; gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); if (!gc_th) return -ENOMEM; -- cgit v1.2.3 From ec7b1f2dd180afb1ce23aa44a7451e59bf2f3eb3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:52:28 +0900 Subject: f2fs: name gc task as per the block device Currently GC task is started for each f2fs formatted/mounted device. But, when we check the task list, using 'ps', there is no distinguishing factor between the tasks. So, name the task as per the block device just like the flusher threads. Also, remove the macro GC_THREAD_NAME and instead use the name: f2fs_gc to avoid name length truncation, as the command length is 16 -> TASK_COMM_LEN 16 and example name like: f2fs_gc_task:8:16 -> this exceeds name length Before Patch for 2 F2FS formatted partitions: root 28061 0.0 0.0 0 0 ? S 10:31 0:00 [f2fs_gc_task] root 28087 0.0 0.0 0 0 ? S 10:32 0:00 [f2fs_gc_task] After Patch: root 16756 0.0 0.0 0 0 ? S 14:57 0:00 [f2fs_gc-8:18] root 16765 0.0 0.0 0 0 ? S 14:57 0:00 [f2fs_gc-8:19] Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 ++- fs/f2fs/gc.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8d293cb685ba..b1e498503e59 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -90,6 +90,7 @@ static int gc_thread_func(void *data) int start_gc_thread(struct f2fs_sb_info *sbi) { struct f2fs_gc_kthread *gc_th; + dev_t dev = sbi->sb->s_bdev->bd_dev; if (!test_opt(sbi, BG_GC)) return 0; @@ -100,7 +101,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi) sbi->gc_thread = gc_th; init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, - GC_THREAD_NAME); + "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(gc_th->f2fs_gc_task)) { kfree(gc_th); return -ENOMEM; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index b026d9354ccd..3abdf83b5c16 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#define GC_THREAD_NAME "f2fs_gc_task" #define GC_THREAD_MIN_WB_PAGES 1 /* * a threshold to determine * whether IO subsystem is idle -- cgit v1.2.3 From 25718423ea6f7118f9c173adff0fac52414571b8 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:52:42 +0900 Subject: f2fs: mark gc_thread as NULL when thread creation is failed When gc thread creation is failed, mark gc_thread as NULL to avoid crash while trying to stop invalid thread in stop_gc_thread->kthread_stop. Instead make it return from: if (!gc_th) return; Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index b1e498503e59..16fdec355201 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -104,6 +104,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi) "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(gc_th->f2fs_gc_task)) { kfree(gc_th); + sbi->gc_thread = NULL; return -ENOMEM; } return 0; -- cgit v1.2.3 From 5ac206cf4f9aad871aa1f1dd653a3404092db56c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:52:59 +0900 Subject: f2fs: make an accessor to get sections for particular block type Introduce accessor to get the sections based upon the block type (node,dents...) and modify the functions : should_do_checkpoint, has_not_enough_free_secs to use this accessor function to get the node sections and dent sections. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 8 ++++++++ fs/f2fs/gc.h | 8 ++------ fs/f2fs/segment.h | 9 ++------- 3 files changed, 12 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5022a7d7f7ca..87840bccaf21 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -573,6 +573,14 @@ static inline int get_pages(struct f2fs_sb_info *sbi, int count_type) return atomic_read(&sbi->nr_pages[count_type]); } +static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type) +{ + unsigned int pages_per_sec = sbi->segs_per_sec * + (1 << sbi->log_blocks_per_seg); + return ((get_pages(sbi, block_type) + pages_per_sec - 1) + >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; +} + static inline block_t valid_user_blocks(struct f2fs_sb_info *sbi) { block_t ret; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 3abdf83b5c16..c407a75a7daa 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -106,11 +106,7 @@ static inline int is_idle(struct f2fs_sb_info *sbi) static inline bool should_do_checkpoint(struct f2fs_sb_info *sbi) { - unsigned int pages_per_sec = sbi->segs_per_sec * - (1 << sbi->log_blocks_per_seg); - int node_secs = ((get_pages(sbi, F2FS_DIRTY_NODES) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - int dent_secs = ((get_pages(sbi, F2FS_DIRTY_DENTS) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; + int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); + int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); return free_sections(sbi) <= (node_secs + 2 * dent_secs + 2); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 66a288a52fd3..a9417b94d6bc 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -459,13 +459,8 @@ static inline int get_ssr_segment(struct f2fs_sb_info *sbi, int type) static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi) { - unsigned int pages_per_sec = (1 << sbi->log_blocks_per_seg) * - sbi->segs_per_sec; - int node_secs = ((get_pages(sbi, F2FS_DIRTY_NODES) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - int dent_secs = ((get_pages(sbi, F2FS_DIRTY_DENTS) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - + int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); + int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); if (sbi->por_doing) return false; -- cgit v1.2.3 From b1f1daf8c72d615b64163e26488d8effeed29b60 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:53:15 +0900 Subject: f2fs: optimize the return condition for has_not_enough_free_secs Instead of evaluating the free_sections and then deciding to return true/false from that path. We can directly use the evaluation condition for returning proper value. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index a9417b94d6bc..458bf5c726f7 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -464,10 +464,8 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi) if (sbi->por_doing) return false; - if (free_sections(sbi) <= (node_secs + 2 * dent_secs + - reserved_sections(sbi))) - return true; - return false; + return (free_sections(sbi) <= (node_secs + 2 * dent_secs + + reserved_sections(sbi))); } static inline int utilization(struct f2fs_sb_info *sbi) -- cgit v1.2.3 From 437275272f9e635673f065300e5d95226a25cb06 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 4 Feb 2013 15:11:17 +0900 Subject: f2fs: clarify and enhance the f2fs_gc flow This patch makes clearer the ambiguous f2fs_gc flow as follows. 1. Remove intermediate checkpoint condition during f2fs_gc (i.e., should_do_checkpoint() and GC_BLOCKED) 2. Remove unnecessary return values of f2fs_gc because of #1. (i.e., GC_NODE, GC_OK, etc) 3. Simplify write_checkpoint() because of #2. 4. Clarify the main f2fs_gc flow. o monitor how many freed sections during one iteration of do_garbage_collect(). o do GC more without checkpoints if we can't get enough free sections. o do checkpoint once we've got enough free sections through forground GCs. 5. Adopt thread-logging (Slack-Space-Recycle) scheme more aggressively on data log types. See. get_ssr_segement() Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++--- fs/f2fs/f2fs.h | 3 +- fs/f2fs/gc.c | 107 ++++++++++++++++++++------------------------------- fs/f2fs/gc.h | 16 -------- fs/f2fs/node.c | 2 +- fs/f2fs/recovery.c | 2 +- fs/f2fs/segment.c | 21 +++++++++- fs/f2fs/segment.h | 12 ++---- fs/f2fs/super.c | 4 +- 9 files changed, 72 insertions(+), 105 deletions(-) (limited to 'fs') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 2887c196b0a2..2b6fc131e2ce 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -539,7 +539,7 @@ retry: /* * Freeze all the FS-operations for checkpoint. */ -void block_operations(struct f2fs_sb_info *sbi) +static void block_operations(struct f2fs_sb_info *sbi) { int t; struct writeback_control wbc = { @@ -722,15 +722,13 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) /* * We guarantee that this checkpoint procedure should not fail. */ -void write_checkpoint(struct f2fs_sb_info *sbi, bool blocked, bool is_umount) +void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); unsigned long long ckpt_ver; - if (!blocked) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - } + mutex_lock(&sbi->cp_mutex); + block_operations(sbi); f2fs_submit_bio(sbi, DATA, true); f2fs_submit_bio(sbi, NODE, true); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 87840bccaf21..e7e7a29767d6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -969,8 +969,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *); void set_dirty_dir_page(struct inode *, struct page *); void remove_dirty_dir_inode(struct inode *); void sync_dirty_dir_inodes(struct f2fs_sb_info *); -void block_operations(struct f2fs_sb_info *); -void write_checkpoint(struct f2fs_sb_info *, bool, bool); +void write_checkpoint(struct f2fs_sb_info *, bool); void init_orphan_info(struct f2fs_sb_info *); int __init create_checkpoint_caches(void); void destroy_checkpoint_caches(void); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 16fdec355201..52d3a391b922 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -78,7 +78,8 @@ static int gc_thread_func(void *data) sbi->bg_gc++; - if (f2fs_gc(sbi) == GC_NONE) + /* if return value is not zero, no victim was selected */ + if (f2fs_gc(sbi)) wait_ms = GC_THREAD_NOGC_SLEEP_TIME; else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME) wait_ms = GC_THREAD_MAX_SLEEP_TIME; @@ -360,7 +361,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, sentry = get_seg_entry(sbi, segno); ret = f2fs_test_bit(offset, sentry->cur_valid_map); mutex_unlock(&sit_i->sentry_lock); - return ret ? GC_OK : GC_NEXT; + return ret; } /* @@ -368,7 +369,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, * On validity, copy that node with cold status, otherwise (invalid node) * ignore that. */ -static int gc_node_segment(struct f2fs_sb_info *sbi, +static void gc_node_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, unsigned int segno, int gc_type) { bool initial = true; @@ -380,21 +381,12 @@ next_step: for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { nid_t nid = le32_to_cpu(entry->nid); struct page *node_page; - int err; - /* - * It makes sure that free segments are able to write - * all the dirty node pages before CP after this CP. - * So let's check the space of dirty node pages. - */ - if (should_do_checkpoint(sbi)) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - return GC_BLOCKED; - } + /* stop BG_GC if there is not enough free sections. */ + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + return; - err = check_valid_map(sbi, segno, off); - if (err == GC_NEXT) + if (check_valid_map(sbi, segno, off) == 0) continue; if (initial) { @@ -424,7 +416,6 @@ next_step: }; sync_node_pages(sbi, 0, &wbc); } - return GC_DONE; } /* @@ -467,13 +458,13 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, node_page = get_node_page(sbi, nid); if (IS_ERR(node_page)) - return GC_NEXT; + return 0; get_node_info(sbi, nid, dni); if (sum->version != dni->version) { f2fs_put_page(node_page, 1); - return GC_NEXT; + return 0; } *nofs = ofs_of_node(node_page); @@ -481,8 +472,8 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, f2fs_put_page(node_page, 1); if (source_blkaddr != blkaddr) - return GC_NEXT; - return GC_OK; + return 0; + return 1; } static void move_data_page(struct inode *inode, struct page *page, int gc_type) @@ -523,13 +514,13 @@ out: * If the parent node is not valid or the data block address is different, * the victim data block is ignored. */ -static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, +static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, struct list_head *ilist, unsigned int segno, int gc_type) { struct super_block *sb = sbi->sb; struct f2fs_summary *entry; block_t start_addr; - int err, off; + int off; int phase = 0; start_addr = START_BLOCK(sbi, segno); @@ -543,20 +534,11 @@ next_step: unsigned int ofs_in_node, nofs; block_t start_bidx; - /* - * It makes sure that free segments are able to write - * all the dirty node pages before CP after this CP. - * So let's check the space of dirty node pages. - */ - if (should_do_checkpoint(sbi)) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - err = GC_BLOCKED; - goto stop; - } + /* stop BG_GC if there is not enough free sections. */ + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + return; - err = check_valid_map(sbi, segno, off); - if (err == GC_NEXT) + if (check_valid_map(sbi, segno, off) == 0) continue; if (phase == 0) { @@ -565,8 +547,7 @@ next_step: } /* Get an inode by ino with checking validity */ - err = check_dnode(sbi, entry, &dni, start_addr + off, &nofs); - if (err == GC_NEXT) + if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0) continue; if (phase == 1) { @@ -606,11 +587,9 @@ next_iput: } if (++phase < 4) goto next_step; - err = GC_DONE; -stop: + if (gc_type == FG_GC) f2fs_submit_bio(sbi, DATA, true); - return err; } static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, @@ -624,17 +603,16 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, return ret; } -static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, +static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, struct list_head *ilist, int gc_type) { struct page *sum_page; struct f2fs_summary_block *sum; - int ret = GC_DONE; /* read segment summary of victim */ sum_page = get_sum_page(sbi, segno); if (IS_ERR(sum_page)) - return GC_ERROR; + return; /* * CP needs to lock sum_page. In this time, we don't need @@ -646,17 +624,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, switch (GET_SUM_TYPE((&sum->footer))) { case SUM_TYPE_NODE: - ret = gc_node_segment(sbi, sum->entries, segno, gc_type); + gc_node_segment(sbi, sum->entries, segno, gc_type); break; case SUM_TYPE_DATA: - ret = gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); + gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); break; } stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer))); stat_inc_call_count(sbi->stat_info); f2fs_put_page(sum_page, 0); - return ret; } int f2fs_gc(struct f2fs_sb_info *sbi) @@ -664,40 +641,38 @@ int f2fs_gc(struct f2fs_sb_info *sbi) struct list_head ilist; unsigned int segno, i; int gc_type = BG_GC; - int gc_status = GC_NONE; + int nfree = 0; + int ret = -1; INIT_LIST_HEAD(&ilist); gc_more: if (!(sbi->sb->s_flags & MS_ACTIVE)) goto stop; - if (gc_type == BG_GC && has_not_enough_free_secs(sbi)) + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) gc_type = FG_GC; if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) goto stop; + ret = 0; - for (i = 0; i < sbi->segs_per_sec; i++) { - /* - * do_garbage_collect will give us three gc_status: - * GC_ERROR, GC_DONE, and GC_BLOCKED. - * If GC is finished uncleanly, we have to return - * the victim to dirty segment list. - */ - gc_status = do_garbage_collect(sbi, segno + i, &ilist, gc_type); - if (gc_status != GC_DONE) - break; - } - if (has_not_enough_free_secs(sbi)) { - write_checkpoint(sbi, (gc_status == GC_BLOCKED), false); - if (has_not_enough_free_secs(sbi)) - goto gc_more; - } + for (i = 0; i < sbi->segs_per_sec; i++) + do_garbage_collect(sbi, segno + i, &ilist, gc_type); + + if (gc_type == FG_GC && + get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0) + nfree++; + + if (has_not_enough_free_secs(sbi, nfree)) + goto gc_more; + + if (gc_type == FG_GC) + write_checkpoint(sbi, false); stop: mutex_unlock(&sbi->gc_mutex); put_gc_inode(&ilist); - return gc_status; + return ret; } void build_gc_manager(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index c407a75a7daa..30b2db003acd 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -22,15 +22,6 @@ /* Search max. number of dirty segments to select a victim segment */ #define MAX_VICTIM_SEARCH 20 -enum { - GC_NONE = 0, - GC_ERROR, - GC_OK, - GC_NEXT, - GC_BLOCKED, - GC_DONE, -}; - struct f2fs_gc_kthread { struct task_struct *f2fs_gc_task; wait_queue_head_t gc_wait_queue_head; @@ -103,10 +94,3 @@ static inline int is_idle(struct f2fs_sb_info *sbi) struct request_list *rl = &q->root_rl; return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]); } - -static inline bool should_do_checkpoint(struct f2fs_sb_info *sbi) -{ - int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); - int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); - return free_sections(sbi) <= (node_secs + 2 * dent_secs + 2); -} diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 33fa6d506d94..43ce16422b75 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1135,7 +1135,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, /* First check balancing cached NAT entries */ if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) { - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); return 0; } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index e2a3e1a8eae9..01e1a03b54c8 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -373,5 +373,5 @@ void recover_fsync_data(struct f2fs_sb_info *sbi) out: destroy_fsync_dnodes(sbi, &inode_list); kmem_cache_destroy(fsync_entry_slab); - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7aa270f3538a..777f17e496e6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -29,7 +29,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi) * We should do GC or end up with checkpoint, if there are so many dirty * dir/node pages without enough free segments. */ - if (has_not_enough_free_secs(sbi)) { + if (has_not_enough_free_secs(sbi, 0)) { mutex_lock(&sbi->gc_mutex); f2fs_gc(sbi); } @@ -308,7 +308,7 @@ static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, * If there is not enough reserved sections, * we should not reuse prefree segments. */ - if (has_not_enough_free_secs(sbi)) + if (has_not_enough_free_secs(sbi, 0)) return NULL_SEGNO; /* @@ -536,6 +536,23 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) } } +static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; + + if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0)) + return v_ops->get_victim(sbi, + &(curseg)->next_segno, BG_GC, type, SSR); + + /* For data segments, let's do SSR more intensively */ + for (; type >= CURSEG_HOT_DATA; type--) + if (v_ops->get_victim(sbi, &(curseg)->next_segno, + BG_GC, type, SSR)) + return 1; + return 0; +} + /* * flush out current segment and replace it with new segment * This function should be returned with success, otherwise BUG diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 458bf5c726f7..552dadbb2327 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -450,21 +450,15 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi) return (free_sections(sbi) < overprovision_sections(sbi)); } -static inline int get_ssr_segment(struct f2fs_sb_info *sbi, int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - return DIRTY_I(sbi)->v_ops->get_victim(sbi, - &(curseg)->next_segno, BG_GC, type, SSR); -} - -static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi) +static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed) { int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + if (sbi->por_doing) return false; - return (free_sections(sbi) <= (node_secs + 2 * dent_secs + + return ((free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + reserved_sections(sbi))); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ddb665f54d17..8c117649a035 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -112,7 +112,7 @@ static void f2fs_put_super(struct super_block *sb) f2fs_destroy_stats(sbi); stop_gc_thread(sbi); - write_checkpoint(sbi, false, true); + write_checkpoint(sbi, true); iput(sbi->node_inode); iput(sbi->meta_inode); @@ -136,7 +136,7 @@ int f2fs_sync_fs(struct super_block *sb, int sync) return 0; if (sync) - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); else f2fs_balance_fs(sbi); -- cgit v1.2.3 From b7250d2d845822466356f7f22a650bf807090d7e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 5 Feb 2013 13:19:28 +0900 Subject: f2fs: fix calculation of max. gc cost in the SSR case In the SSR case, the max gc cost should be the number of pages in a segment. Otherwise, f2fs is able to fail getting dirty segments frequently for SSR. In get_victim_by_default() previously, while(1) { ... cost = get_gc_cost(); <- cost is between 0 ~ 512. ... if (cost == get_max_cost(sbi, &p)) <- max cost is UINT_MAX due to GC_CB type continue; if (nsearched++ >= MAX_VICTIM_SEARCH) break; } So, if there are a number of fully valid segments in series, f2fs cannot skip those segments by comparing the cost and max cost of each segment. Note that, the cost is the number of valid blocks at the time of the last checkpoint. Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 52d3a391b922..94b8a0c48453 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -146,6 +146,9 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type, static unsigned int get_max_cost(struct f2fs_sb_info *sbi, struct victim_sel_policy *p) { + /* SSR allocates in a segment unit */ + if (p->alloc_mode == SSR) + return 1 << sbi->log_blocks_per_seg; if (p->gc_mode == GC_GREEDY) return (1 << sbi->log_blocks_per_seg) * p->ofs_unit; else if (p->gc_mode == GC_CB) -- cgit v1.2.3 From e9750824114ff939d9da299e73651add6aa65456 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 4 Feb 2013 23:41:41 +0900 Subject: f2fs: add compat_ioctl to provide backward compatability adding compat_ioctl to provide support for backward comptability - 32bit binary execution on 64bit kernel. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 15 +++++++++++++++ fs/f2fs/file.c | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'fs') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e7e7a29767d6..c3462b69917e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -103,6 +103,20 @@ static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i) return before; } +/* + * ioctl commands + */ +#define F2FS_IOC_GETFLAGS FS_IOC_GETFLAGS +#define F2FS_IOC_SETFLAGS FS_IOC_SETFLAGS + +#if defined(__KERNEL__) && defined(CONFIG_COMPAT) +/* + * ioctl commands in 32 bit emulation + */ +#define F2FS_IOC32_GETFLAGS FS_IOC32_GETFLAGS +#define F2FS_IOC32_SETFLAGS FS_IOC32_SETFLAGS +#endif + /* * For INODE and NODE manager */ @@ -850,6 +864,7 @@ void f2fs_truncate(struct inode *); int f2fs_setattr(struct dentry *, struct iattr *); int truncate_hole(struct inode *, pgoff_t, pgoff_t); long f2fs_ioctl(struct file *, unsigned int, unsigned long); +long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long); /* * inode.c diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 33d1736ee5f9..b7a053d4c6d3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -634,6 +635,23 @@ out: } } +#ifdef CONFIG_COMPAT +long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case F2FS_IOC32_GETFLAGS: + cmd = F2FS_IOC_GETFLAGS; + break; + case F2FS_IOC32_SETFLAGS: + cmd = F2FS_IOC_SETFLAGS; + break; + default: + return -ENOIOCTLCMD; + } + return f2fs_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); +} +#endif + const struct file_operations f2fs_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, @@ -645,6 +663,9 @@ const struct file_operations f2fs_file_operations = { .fsync = f2fs_sync_file, .fallocate = f2fs_fallocate, .unlocked_ioctl = f2fs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = f2fs_compat_ioctl, +#endif .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; -- cgit v1.2.3 From 7dd690c82029ed34aafdb58ce7463cdead69abb5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Feb 2013 07:28:55 +0900 Subject: f2fs: avoid build warning This patch removes the following build warning: fs/f2fs/node.c: warning: 'nofs' may be used uninitialized in this function [-Wuninitialized]: => 738:8 Note that this is a false alarm. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 0da252c78af8..e275218904ed 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -660,7 +660,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from) struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); int err = 0, cont = 1; int level, offset[4], noffset[4]; - unsigned int nofs; + unsigned int nofs = 0; struct f2fs_node *rn; struct dnode_of_data dn; struct page *page; -- cgit v1.2.3