Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88b88a66 authored by Jaegeuk Kim's avatar Jaegeuk Kim
Browse files

f2fs: support atomic writes



This patch introduces a very limited functionality for atomic write support.
In order to support atomic write, this patch adds two ioctls:
 o F2FS_IOC_START_ATOMIC_WRITE
 o F2FS_IOC_COMMIT_ATOMIC_WRITE

The database engine should be aware of the following sequence.
1. open
 -> ioctl(F2FS_IOC_START_ATOMIC_WRITE);
2. writes
  : all the written data will be treated as atomic pages.
3. commit
 -> ioctl(F2FS_IOC_COMMIT_ATOMIC_WRITE);
  : this flushes all the data blocks to the disk, which will be shown all or
  nothing by f2fs recovery procedure.
4. repeat to #2.

The IO pattens should be:

  ,- START_ATOMIC_WRITE                  ,- COMMIT_ATOMIC_WRITE
 CP | D D D D D D | FSYNC | D D D D | FSYNC ...
                      `- COMMIT_ATOMIC_WRITE

Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 120c2cba
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -1052,6 +1052,9 @@ static int f2fs_write_end(struct file *file,

	trace_f2fs_write_end(inode, pos, len, copied);

	if (f2fs_is_atomic_file(inode))
		register_inmem_page(inode, page);
	else
		set_page_dirty(page);

	if (pos + copied > i_size_read(inode)) {
+18 −3
Original line number Diff line number Diff line
@@ -195,6 +195,10 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
#define F2FS_IOC_GETFLAGS		FS_IOC_GETFLAGS
#define F2FS_IOC_SETFLAGS		FS_IOC_SETFLAGS

#define F2FS_IOCTL_MAGIC		0xf5
#define F2FS_IOC_START_ATOMIC_WRITE	_IO(F2FS_IOCTL_MAGIC, 1)
#define F2FS_IOC_COMMIT_ATOMIC_WRITE	_IO(F2FS_IOCTL_MAGIC, 2)

#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
/*
 * ioctl commands in 32 bit emulation
@@ -263,6 +267,9 @@ struct f2fs_inode_info {
	unsigned long long xattr_ver;	/* cp version of xattr modification */
	struct extent_info ext;		/* in-memory extent cache entry */
	struct dir_inode_entry *dirty_dir;	/* the pointer of dirty dir */

	struct list_head inmem_pages;	/* inmemory pages managed by f2fs */
	struct mutex inmem_lock;	/* lock for inmemory pages */
};

static inline void get_extent_info(struct extent_info *ext,
@@ -1051,7 +1058,8 @@ enum {
	FI_INLINE_DATA,		/* used for inline data*/
	FI_APPEND_WRITE,	/* inode has appended data */
	FI_UPDATE_WRITE,	/* inode has in-place-update data */
	FI_NEED_IPU,		/* used fo ipu for fdatasync */
	FI_NEED_IPU,		/* used for ipu per file */
	FI_ATOMIC_FILE,		/* indicate atomic file */
};

static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
@@ -1138,6 +1146,11 @@ static inline int f2fs_has_inline_data(struct inode *inode)
	return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DATA);
}

static inline bool f2fs_is_atomic_file(struct inode *inode)
{
	return is_inode_flag_set(F2FS_I(inode), FI_ATOMIC_FILE);
}

static inline void *inline_data_addr(struct page *page)
{
	struct f2fs_inode *ri = F2FS_INODE(page);
@@ -1275,6 +1288,8 @@ void destroy_node_manager_caches(void);
/*
 * segment.c
 */
void register_inmem_page(struct inode *, struct page *);
void commit_inmem_pages(struct inode *, bool);
void f2fs_balance_fs(struct f2fs_sb_info *);
void f2fs_balance_fs_bg(struct f2fs_sb_info *);
int f2fs_issue_flush(struct f2fs_sb_info *);
+39 −0
Original line number Diff line number Diff line
@@ -862,6 +862,41 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)
	return ret;
}

static int f2fs_ioc_start_atomic_write(struct file *filp)
{
	struct inode *inode = file_inode(filp);
	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);

	if (!inode_owner_or_capable(inode))
		return -EACCES;

	f2fs_balance_fs(sbi);

	set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);

	return f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, NULL);
}

static int f2fs_ioc_commit_atomic_write(struct file *filp)
{
	struct inode *inode = file_inode(filp);
	int ret;

	if (!inode_owner_or_capable(inode))
		return -EACCES;

	ret = mnt_want_write_file(filp);
	if (ret)
		return ret;

	if (f2fs_is_atomic_file(inode))
		commit_inmem_pages(inode, false);

	ret = f2fs_sync_file(filp, 0, LONG_MAX, 0);
	mnt_drop_write_file(filp);
	return ret;
}

static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
{
	struct inode *inode = file_inode(filp);
@@ -899,6 +934,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
		return f2fs_ioc_getflags(filp, arg);
	case F2FS_IOC_SETFLAGS:
		return f2fs_ioc_setflags(filp, arg);
	case F2FS_IOC_START_ATOMIC_WRITE:
		return f2fs_ioc_start_atomic_write(filp);
	case F2FS_IOC_COMMIT_ATOMIC_WRITE:
		return f2fs_ioc_commit_atomic_write(filp);
	case FITRIM:
		return f2fs_ioc_fitrim(filp, arg);
	default:
+3 −0
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ bool f2fs_may_inline(struct inode *inode)
	if (!test_opt(F2FS_I_SB(inode), INLINE_DATA))
		return false;

	if (f2fs_is_atomic_file(inode))
		return false;

	nr_blocks = F2FS_I(inode)->i_xattr_nid ? 3 : 2;
	if (inode->i_blocks > nr_blocks)
		return false;
+4 −0
Original line number Diff line number Diff line
@@ -269,6 +269,10 @@ void f2fs_evict_inode(struct inode *inode)
	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
	nid_t xnid = F2FS_I(inode)->i_xattr_nid;

	/* some remained atomic pages should discarded */
	if (f2fs_is_atomic_file(inode))
		commit_inmem_pages(inode, true);

	trace_f2fs_evict_inode(inode);
	truncate_inode_pages_final(&inode->i_data);

Loading