Commit 3f490f7f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -98,8 +98,13 @@ Cleaning Overhead
MOUNT OPTIONS
================================================================================

background_gc_off      Turn off cleaning operations, namely garbage collection,
		       triggered in background when I/O subsystem is idle.
background_gc=%s       Turn on/off cleaning operations, namely garbage
                       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
discard                Issue discard/TRIM commands when a segment is cleaned.
no_heap                Disable heap-style segment allocation which finds free
+12 −0
Original line number Diff line number Diff line
@@ -51,3 +51,15 @@ config F2FS_FS_POSIX_ACL
	  Linux website <http://acl.bestbits.at/>.

	  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.
+1 −1
Original line number Diff line number Diff line
@@ -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);
	if (!error)
+76 −23
Original line number Diff line number Diff line
@@ -357,8 +357,8 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
	unsigned long blk_size = sbi->blocksize;
	struct f2fs_checkpoint *cp_block;
	unsigned long long cur_version = 0, pre_version = 0;
	unsigned int crc = 0;
	size_t crc_offset;
	__u32 crc = 0;

	/* Read the 1st cp block in this CP pack */
	cp_page_1 = get_meta_page(sbi, cp_addr);
@@ -369,7 +369,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
	if (crc_offset >= blk_size)
		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))
		goto invalid_cp1;

@@ -384,7 +384,7 @@ static struct page *validate_checkpoint(struct f2fs_sb_info *sbi,
	if (crc_offset >= blk_size)
		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))
		goto invalid_cp2;

@@ -450,13 +450,30 @@ fail_no_cp:
	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 list_head *head = &sbi->dir_inode_list;
	struct dir_inode_entry *new;
	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))
		return;
retry:
@@ -469,23 +486,31 @@ retry:
	INIT_LIST_HEAD(&new->list);

	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 == inode) {
	if (__add_dirty_inode(inode, new))
		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);
	inode_inc_dirty_dents(inode);
	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);
}

@@ -499,8 +524,10 @@ void remove_dirty_dir_inode(struct inode *inode)
		return;

	spin_lock(&sbi->dir_inode_lock);
	if (atomic_read(&F2FS_I(inode)->dirty_dents))
		goto out;
	if (atomic_read(&F2FS_I(inode)->dirty_dents)) {
		spin_unlock(&sbi->dir_inode_lock);
		return;
	}

	list_for_each(this, head) {
		struct dir_inode_entry *entry;
@@ -508,12 +535,38 @@ void remove_dirty_dir_inode(struct inode *inode)
		if (entry->inode == inode) {
			list_del(&entry->list);
			kmem_cache_free(inode_entry_slab, entry);
#ifdef CONFIG_F2FS_STAT_FS
			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;
		}
	}
out:
	spin_unlock(&sbi->dir_inode_lock);
	return inode;
}

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)
	block_t start_blk;
	struct page *cp_page;
	unsigned int data_sum_blocks, orphan_blocks;
	unsigned int crc32 = 0;
	__u32 crc32 = 0;
	void *kaddr;
	int i;

@@ -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));

	crc32 = f2fs_crc32(ckpt, le32_to_cpu(ckpt->checksum_offset));
	*(__le32 *)((unsigned char *)ckpt +
				le32_to_cpu(ckpt->checksum_offset))
	*((__le32 *)((unsigned char *)ckpt +
				le32_to_cpu(ckpt->checksum_offset)))
				= cpu_to_le32(crc32);

	start_blk = __start_cp_addr(sbi);
+53 −15
Original line number Diff line number Diff line
@@ -68,7 +68,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs,
					struct buffer_head *bh_result)
{
	struct f2fs_inode_info *fi = F2FS_I(inode);
#ifdef CONFIG_F2FS_STAT_FS
	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
#endif
	pgoff_t start_fofs, end_fofs;
	block_t start_blkaddr;

@@ -78,7 +80,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs,
		return 0;
	}

#ifdef CONFIG_F2FS_STAT_FS
	sbi->total_hit_ext++;
#endif
	start_fofs = fi->ext.fofs;
	end_fofs = fi->ext.fofs + fi->ext.len - 1;
	start_blkaddr = fi->ext.blk_addr;
@@ -96,7 +100,9 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs,
		else
			bh_result->b_size = UINT_MAX;

#ifdef CONFIG_F2FS_STAT_FS
		sbi->read_hit_ext++;
#endif
		read_unlock(&fi->ext.ext_lock);
		return 1;
	}
@@ -199,7 +205,7 @@ struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync)
	if (dn.data_blkaddr == NEW_ADDR)
		return ERR_PTR(-EINVAL);

	page = grab_cache_page(mapping, index);
	page = grab_cache_page_write_begin(mapping, index, AOP_FLAG_NOFS);
	if (!page)
		return ERR_PTR(-ENOMEM);

@@ -233,18 +239,23 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index)
	struct page *page;
	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);
	err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
	if (err)
	if (err) {
		f2fs_put_page(page, 1);
		return ERR_PTR(err);
	}
	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);
repeat:
	page = grab_cache_page(mapping, index);
	if (!page)
		return ERR_PTR(-ENOMEM);
	}

	if (PageUptodate(page))
		return page;
@@ -274,9 +285,10 @@ repeat:
 *
 * Also, caller should grab and release a mutex by calling mutex_lock_op() and
 * 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,
						bool new_i_size)
struct page *get_new_data_page(struct inode *inode,
		struct page *npage, pgoff_t index, bool new_i_size)
{
	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
	struct address_space *mapping = inode->i_mapping;
@@ -284,17 +296,19 @@ struct page *get_new_data_page(struct inode *inode, pgoff_t index,
	struct dnode_of_data dn;
	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);
	if (err)
		return ERR_PTR(err);

	if (dn.data_blkaddr == NULL_ADDR) {
		if (reserve_new_block(&dn)) {
			if (!npage)
				f2fs_put_dnode(&dn);
			return ERR_PTR(-ENOSPC);
		}
	}
	if (!npage)
		f2fs_put_dnode(&dn);
repeat:
	page = grab_cache_page(mapping, index);
@@ -325,6 +339,8 @@ repeat:
	if (new_i_size &&
		i_size_read(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);
	}
	return page;
@@ -481,8 +497,9 @@ int do_write_data_page(struct page *page)
	 * If current allocation needs SSR,
	 * it had better in-place writes for updated data.
	 */
	if (old_blk_addr != NEW_ADDR && !is_cold_data(page) &&
				need_inplace_update(inode)) {
	if (unlikely(old_blk_addr != NEW_ADDR &&
			!is_cold_data(page) &&
			need_inplace_update(inode))) {
		rewrite_data_page(F2FS_SB(inode->i_sb), page,
						old_blk_addr);
	} else {
@@ -684,6 +701,27 @@ 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,
		const struct iovec *iov, loff_t offset, unsigned long nr_segs)
{
@@ -741,7 +779,7 @@ const struct address_space_operations f2fs_dblock_aops = {
	.writepage	= f2fs_write_data_page,
	.writepages	= f2fs_write_data_pages,
	.write_begin	= f2fs_write_begin,
	.write_end	= nobh_write_end,
	.write_end	= f2fs_write_end,
	.set_page_dirty	= f2fs_set_data_page_dirty,
	.invalidatepage	= f2fs_invalidate_data_page,
	.releasepage	= f2fs_release_data_page,
Loading