Commit 9ffe0fb5 authored by Huajun Li's avatar Huajun Li Committed by Jaegeuk Kim

f2fs: handle inline data operations

Hook inline data read/write, truncate, fallocate, setattr, etc.

Files need meet following 2 requirement to inline:
 1) file size is not greater than MAX_INLINE_DATA;
 2) file doesn't pre-allocate data blocks by fallocate().

FI_INLINE_DATA will not be set while creating a new regular inode because
most of the files are bigger than ~3.4K. Set FI_INLINE_DATA only when
data is submitted to block layer, ranther than set it while creating a new
inode, this also avoids converting data from inline to normal data block
and vice versa.

While writting inline data to inode block, the first data block should be
released if the file has a block indexed by i_addr[0].

On the other hand, when a file operation is appied to a file with inline
data, we need to test if this file can remain inline by doing this
operation, otherwise it should be convert into normal file by reserving
a new data block, copying inline data to this new block and clear
FI_INLINE_DATA flag. Because reserve a new data block here will make use
of i_addr[0], if we save inline data in i_addr[0..872], then the first
4 bytes would be overwriten. This problem can be avoided simply by
not using i_addr[0] for inline data.
Signed-off-by: default avatarHuajun Li <huajun.li@intel.com>
Signed-off-by: default avatarHaicheng Li <haicheng.li@linux.intel.com>
Signed-off-by: default avatarWeihong Xu <weihong.xu@intel.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk.kim@samsung.com>
parent e18c65b2
...@@ -706,13 +706,28 @@ static int get_data_block(struct inode *inode, sector_t iblock, ...@@ -706,13 +706,28 @@ static int get_data_block(struct inode *inode, sector_t iblock,
static int f2fs_read_data_page(struct file *file, struct page *page) static int f2fs_read_data_page(struct file *file, struct page *page)
{ {
return mpage_readpage(page, get_data_block); struct inode *inode = page->mapping->host;
int ret;
/* If the file has inline data, try to read it directlly */
if (f2fs_has_inline_data(inode))
ret = f2fs_read_inline_data(inode, page);
else
ret = mpage_readpage(page, get_data_block);
return ret;
} }
static int f2fs_read_data_pages(struct file *file, static int f2fs_read_data_pages(struct file *file,
struct address_space *mapping, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages) struct list_head *pages, unsigned nr_pages)
{ {
struct inode *inode = file->f_mapping->host;
/* If the file has inline data, skip readpages */
if (f2fs_has_inline_data(inode))
return 0;
return mpage_readpages(mapping, pages, nr_pages, get_data_block); return mpage_readpages(mapping, pages, nr_pages, get_data_block);
} }
...@@ -761,7 +776,7 @@ static int f2fs_write_data_page(struct page *page, ...@@ -761,7 +776,7 @@ static int f2fs_write_data_page(struct page *page,
loff_t i_size = i_size_read(inode); loff_t i_size = i_size_read(inode);
const pgoff_t end_index = ((unsigned long long) i_size) const pgoff_t end_index = ((unsigned long long) i_size)
>> PAGE_CACHE_SHIFT; >> PAGE_CACHE_SHIFT;
unsigned offset; unsigned offset = 0;
bool need_balance_fs = false; bool need_balance_fs = false;
int err = 0; int err = 0;
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
...@@ -799,7 +814,15 @@ static int f2fs_write_data_page(struct page *page, ...@@ -799,7 +814,15 @@ static int f2fs_write_data_page(struct page *page,
err = do_write_data_page(page, &fio); err = do_write_data_page(page, &fio);
} else { } else {
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
err = do_write_data_page(page, &fio);
if (f2fs_has_inline_data(inode) || f2fs_may_inline(inode)) {
err = f2fs_write_inline_data(inode, page, offset);
f2fs_unlock_op(sbi);
goto out;
} else {
err = do_write_data_page(page, &fio);
}
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
need_balance_fs = true; need_balance_fs = true;
} }
...@@ -888,6 +911,15 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, ...@@ -888,6 +911,15 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
return -ENOMEM; return -ENOMEM;
*pagep = page; *pagep = page;
if ((pos + len) < MAX_INLINE_DATA) {
if (f2fs_has_inline_data(inode))
goto inline_data;
} else if (f2fs_has_inline_data(inode)) {
err = f2fs_convert_inline_data(inode, page, flags);
if (err)
return err;
}
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = f2fs_reserve_block(&dn, index); err = f2fs_reserve_block(&dn, index);
...@@ -897,7 +929,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, ...@@ -897,7 +929,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
f2fs_put_page(page, 1); f2fs_put_page(page, 1);
return err; return err;
} }
inline_data:
if ((len == PAGE_CACHE_SIZE) || PageUptodate(page)) if ((len == PAGE_CACHE_SIZE) || PageUptodate(page))
return 0; return 0;
...@@ -913,7 +945,10 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, ...@@ -913,7 +945,10 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
if (dn.data_blkaddr == NEW_ADDR) { if (dn.data_blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE); zero_user_segment(page, 0, PAGE_CACHE_SIZE);
} else { } else {
err = f2fs_submit_page_bio(sbi, page, dn.data_blkaddr, if (f2fs_has_inline_data(inode))
err = f2fs_read_inline_data(inode, page);
else
err = f2fs_submit_page_bio(sbi, page, dn.data_blkaddr,
READ_SYNC); READ_SYNC);
if (err) if (err)
return err; return err;
...@@ -977,6 +1012,10 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, ...@@ -977,6 +1012,10 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
struct file *file = iocb->ki_filp; struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
/* Let buffer I/O handle the inline data case. */
if (f2fs_has_inline_data(inode))
return 0;
if (check_direct_IO(inode, rw, iov, offset, nr_segs)) if (check_direct_IO(inode, rw, iov, offset, nr_segs))
return 0; return 0;
......
...@@ -257,8 +257,7 @@ static int truncate_blocks(struct inode *inode, u64 from) ...@@ -257,8 +257,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
unsigned int blocksize = inode->i_sb->s_blocksize; unsigned int blocksize = inode->i_sb->s_blocksize;
struct dnode_of_data dn; struct dnode_of_data dn;
pgoff_t free_from; pgoff_t free_from;
int count = 0; int count = 0, err = 0;
int err;
trace_f2fs_truncate_blocks_enter(inode, from); trace_f2fs_truncate_blocks_enter(inode, from);
...@@ -266,6 +265,10 @@ static int truncate_blocks(struct inode *inode, u64 from) ...@@ -266,6 +265,10 @@ static int truncate_blocks(struct inode *inode, u64 from)
((from + blocksize - 1) >> (sbi->log_blocksize)); ((from + blocksize - 1) >> (sbi->log_blocksize));
f2fs_lock_op(sbi); f2fs_lock_op(sbi);
if (f2fs_has_inline_data(inode))
goto done;
set_new_dnode(&dn, inode, NULL, NULL, 0); set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE); err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE);
if (err) { if (err) {
...@@ -292,6 +295,7 @@ static int truncate_blocks(struct inode *inode, u64 from) ...@@ -292,6 +295,7 @@ static int truncate_blocks(struct inode *inode, u64 from)
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
free_next: free_next:
err = truncate_inode_blocks(inode, free_from); err = truncate_inode_blocks(inode, free_from);
done:
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
/* lastly zero out the first data page */ /* lastly zero out the first data page */
...@@ -367,8 +371,17 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -367,8 +371,17 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
if ((attr->ia_valid & ATTR_SIZE) && if ((attr->ia_valid & ATTR_SIZE) &&
attr->ia_size != i_size_read(inode)) { attr->ia_size != i_size_read(inode)) {
if (f2fs_has_inline_data(inode) &&
(attr->ia_size > MAX_INLINE_DATA)) {
unsigned flags = AOP_FLAG_NOFS;
err = f2fs_convert_inline_data(inode, NULL, flags);
if (err)
return err;
}
truncate_setsize(inode, attr->ia_size); truncate_setsize(inode, attr->ia_size);
f2fs_truncate(inode); if (!f2fs_has_inline_data(inode))
f2fs_truncate(inode);
f2fs_balance_fs(F2FS_SB(inode->i_sb)); f2fs_balance_fs(F2FS_SB(inode->i_sb));
} }
...@@ -450,6 +463,26 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len) ...@@ -450,6 +463,26 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
loff_t off_start, off_end; loff_t off_start, off_end;
int ret = 0; int ret = 0;
if (f2fs_has_inline_data(inode)) {
struct page *page;
unsigned flags = AOP_FLAG_NOFS;
page = grab_cache_page_write_begin(inode->i_mapping, 0, flags);
if (IS_ERR(page))
return PTR_ERR(page);
if (offset + len > MAX_INLINE_DATA) {
ret = f2fs_convert_inline_data(inode, page, flags);
f2fs_put_page(page, 1);
if (ret)
return ret;
} else {
zero_user_segment(page, offset, offset + len);
SetPageUptodate(page);
set_page_dirty(page);
f2fs_put_page(page, 1);
return ret;
}
}
pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
...@@ -496,6 +529,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset, ...@@ -496,6 +529,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
loff_t off_start, off_end; loff_t off_start, off_end;
int ret = 0; int ret = 0;
if (f2fs_has_inline_data(inode) && (offset + len > MAX_INLINE_DATA)) {
ret = f2fs_convert_inline_data(inode, NULL, AOP_FLAG_NOFS);
if (ret)
return ret;
}
ret = inode_newsize_ok(inode, (len + offset)); ret = inode_newsize_ok(inode, (len + offset));
if (ret) if (ret)
return ret; return ret;
......
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