Commit 1e84371f authored by Jaegeuk Kim's avatar Jaegeuk Kim

f2fs: change atomic and volatile write policies

This patch adds two new ioctls to release inmemory pages grabbed by atomic
writes.
 o f2fs_ioc_abort_volatile_write
  - If transaction was failed, all the grabbed pages and data should be written.
 o f2fs_ioc_release_volatile_write
  - This is to enhance the performance of PERSIST mode in sqlite.

In order to avoid huge memory consumption which causes OOM, this patch changes
volatile writes to use normal dirty pages, instead blocked flushing to the disk
as long as system does not suffer from memory pressure.
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 70c640b1
...@@ -814,6 +814,11 @@ static int f2fs_write_data_page(struct page *page, ...@@ -814,6 +814,11 @@ static int f2fs_write_data_page(struct page *page,
write: write:
if (unlikely(sbi->por_doing)) if (unlikely(sbi->por_doing))
goto redirty_out; goto redirty_out;
if (f2fs_is_drop_cache(inode))
goto out;
if (f2fs_is_volatile_file(inode) && !wbc->for_reclaim &&
available_free_memory(sbi, BASE_CHECK))
goto redirty_out;
/* Dentry blocks are controlled by checkpoint */ /* Dentry blocks are controlled by checkpoint */
if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
...@@ -1109,7 +1114,7 @@ static void f2fs_invalidate_data_page(struct page *page, unsigned int offset, ...@@ -1109,7 +1114,7 @@ static void f2fs_invalidate_data_page(struct page *page, unsigned int offset,
if (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE) if (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE)
return; return;
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode)) if (f2fs_is_atomic_file(inode))
invalidate_inmem_page(inode, page); invalidate_inmem_page(inode, page);
if (PageDirty(page)) if (PageDirty(page))
...@@ -1132,7 +1137,7 @@ static int f2fs_set_data_page_dirty(struct page *page) ...@@ -1132,7 +1137,7 @@ static int f2fs_set_data_page_dirty(struct page *page)
SetPageUptodate(page); SetPageUptodate(page);
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode)) { if (f2fs_is_atomic_file(inode)) {
register_inmem_page(inode, page); register_inmem_page(inode, page);
return 1; return 1;
} }
......
...@@ -201,6 +201,8 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, ...@@ -201,6 +201,8 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
#define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1) #define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1)
#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2) #define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2)
#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3) #define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3)
#define F2FS_IOC_RELEASE_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 4)
#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
#if defined(__KERNEL__) && defined(CONFIG_COMPAT) #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
/* /*
...@@ -1113,6 +1115,7 @@ enum { ...@@ -1113,6 +1115,7 @@ enum {
FI_NEED_IPU, /* used for ipu per file */ FI_NEED_IPU, /* used for ipu per file */
FI_ATOMIC_FILE, /* indicate atomic file */ FI_ATOMIC_FILE, /* indicate atomic file */
FI_VOLATILE_FILE, /* indicate volatile file */ FI_VOLATILE_FILE, /* indicate volatile file */
FI_DROP_CACHE, /* drop dirty page cache */
FI_DATA_EXIST, /* indicate data exists */ FI_DATA_EXIST, /* indicate data exists */
}; };
...@@ -1220,6 +1223,11 @@ static inline bool f2fs_is_volatile_file(struct inode *inode) ...@@ -1220,6 +1223,11 @@ static inline bool f2fs_is_volatile_file(struct inode *inode)
return is_inode_flag_set(F2FS_I(inode), FI_VOLATILE_FILE); return is_inode_flag_set(F2FS_I(inode), FI_VOLATILE_FILE);
} }
static inline bool f2fs_is_drop_cache(struct inode *inode)
{
return is_inode_flag_set(F2FS_I(inode), FI_DROP_CACHE);
}
static inline void *inline_data_addr(struct page *page) static inline void *inline_data_addr(struct page *page)
{ {
struct f2fs_inode *ri = F2FS_INODE(page); struct f2fs_inode *ri = F2FS_INODE(page);
......
...@@ -836,6 +836,19 @@ static long f2fs_fallocate(struct file *file, int mode, ...@@ -836,6 +836,19 @@ static long f2fs_fallocate(struct file *file, int mode,
return ret; return ret;
} }
static int f2fs_release_file(struct inode *inode, struct file *filp)
{
/* some remained atomic pages should discarded */
if (f2fs_is_atomic_file(inode))
commit_inmem_pages(inode, true);
if (f2fs_is_volatile_file(inode)) {
set_inode_flag(F2FS_I(inode), FI_DROP_CACHE);
filemap_fdatawrite(inode->i_mapping);
clear_inode_flag(F2FS_I(inode), FI_DROP_CACHE);
}
return 0;
}
#define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) #define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL))
#define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) #define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL)
...@@ -909,26 +922,20 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) ...@@ -909,26 +922,20 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg)
static int f2fs_ioc_start_atomic_write(struct file *filp) static int f2fs_ioc_start_atomic_write(struct file *filp)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
if (!inode_owner_or_capable(inode)) if (!inode_owner_or_capable(inode))
return -EACCES; return -EACCES;
f2fs_balance_fs(sbi); f2fs_balance_fs(F2FS_I_SB(inode));
if (f2fs_is_atomic_file(inode))
return 0;
set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
return f2fs_convert_inline_inode(inode); return f2fs_convert_inline_inode(inode);
} }
static int f2fs_release_file(struct inode *inode, struct file *filp)
{
/* some remained atomic pages should discarded */
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode))
commit_inmem_pages(inode, true);
return 0;
}
static int f2fs_ioc_commit_atomic_write(struct file *filp) static int f2fs_ioc_commit_atomic_write(struct file *filp)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
...@@ -949,6 +956,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp) ...@@ -949,6 +956,7 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)
ret = f2fs_sync_file(filp, 0, LONG_MAX, 0); ret = f2fs_sync_file(filp, 0, LONG_MAX, 0);
mnt_drop_write_file(filp); mnt_drop_write_file(filp);
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
return ret; return ret;
} }
...@@ -959,11 +967,56 @@ static int f2fs_ioc_start_volatile_write(struct file *filp) ...@@ -959,11 +967,56 @@ static int f2fs_ioc_start_volatile_write(struct file *filp)
if (!inode_owner_or_capable(inode)) if (!inode_owner_or_capable(inode))
return -EACCES; return -EACCES;
if (f2fs_is_volatile_file(inode))
return 0;
set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
return f2fs_convert_inline_inode(inode); return f2fs_convert_inline_inode(inode);
} }
static int f2fs_ioc_release_volatile_write(struct file *filp)
{
struct inode *inode = file_inode(filp);
if (!inode_owner_or_capable(inode))
return -EACCES;
if (!f2fs_is_volatile_file(inode))
return 0;
punch_hole(inode, 0, F2FS_BLKSIZE);
return 0;
}
static int f2fs_ioc_abort_volatile_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;
f2fs_balance_fs(F2FS_I_SB(inode));
if (f2fs_is_atomic_file(inode)) {
commit_inmem_pages(inode, false);
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
}
if (f2fs_is_volatile_file(inode)) {
clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
filemap_fdatawrite(inode->i_mapping);
set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
}
mnt_drop_write_file(filp);
return ret;
}
static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
...@@ -1007,6 +1060,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -1007,6 +1060,10 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return f2fs_ioc_commit_atomic_write(filp); return f2fs_ioc_commit_atomic_write(filp);
case F2FS_IOC_START_VOLATILE_WRITE: case F2FS_IOC_START_VOLATILE_WRITE:
return f2fs_ioc_start_volatile_write(filp); return f2fs_ioc_start_volatile_write(filp);
case F2FS_IOC_RELEASE_VOLATILE_WRITE:
return f2fs_ioc_release_volatile_write(filp);
case F2FS_IOC_ABORT_VOLATILE_WRITE:
return f2fs_ioc_abort_volatile_write(filp);
case FITRIM: case FITRIM:
return f2fs_ioc_fitrim(filp, arg); return f2fs_ioc_fitrim(filp, arg);
default: default:
......
...@@ -304,7 +304,7 @@ void f2fs_evict_inode(struct inode *inode) ...@@ -304,7 +304,7 @@ void f2fs_evict_inode(struct inode *inode)
nid_t xnid = F2FS_I(inode)->i_xattr_nid; nid_t xnid = F2FS_I(inode)->i_xattr_nid;
/* some remained atomic pages should discarded */ /* some remained atomic pages should discarded */
if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode)) if (f2fs_is_atomic_file(inode))
commit_inmem_pages(inode, true); commit_inmem_pages(inode, true);
trace_f2fs_evict_inode(inode); trace_f2fs_evict_inode(inode);
......
...@@ -61,6 +61,9 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) ...@@ -61,6 +61,9 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type)
mem_size += (sbi->im[i].ino_num * mem_size += (sbi->im[i].ino_num *
sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT; sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT;
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);
} else {
if (sbi->sb->s_bdi->dirty_exceeded)
return false;
} }
return res; return res;
} }
......
...@@ -108,6 +108,7 @@ enum mem_type { ...@@ -108,6 +108,7 @@ enum mem_type {
NAT_ENTRIES, /* indicates the cached nat entry */ NAT_ENTRIES, /* indicates the cached nat entry */
DIRTY_DENTS, /* indicates dirty dentry pages */ DIRTY_DENTS, /* indicates dirty dentry pages */
INO_ENTRIES, /* indicates inode entries */ INO_ENTRIES, /* indicates inode entries */
BASE_CHECK, /* check kernel status */
}; };
struct nat_entry_set { struct nat_entry_set {
......
...@@ -230,7 +230,7 @@ void commit_inmem_pages(struct inode *inode, bool abort) ...@@ -230,7 +230,7 @@ void commit_inmem_pages(struct inode *inode, bool abort)
bool submit_bio = false; bool submit_bio = false;
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
.type = DATA, .type = DATA,
.rw = WRITE_SYNC, .rw = WRITE_SYNC | REQ_PRIO,
}; };
/* /*
......
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