f2fs: revisit inline_data to avoid data races and potential bugs
[GitHub/exynos8895/android_kernel_samsung_universal8895.git] / fs / f2fs / file.c
index 8e68bb64f83598360ffa8fdb31010aa1c33248f5..832bd91922b8057b5173907846a4cba1036f5dea 100644 (file)
@@ -41,18 +41,18 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
 
        sb_start_pagefault(inode->i_sb);
 
-       /* force to convert with normal data indices */
-       err = f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, page);
-       if (err)
-               goto out;
+       f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
 
        /* block allocation */
        f2fs_lock_op(sbi);
        set_new_dnode(&dn, inode, NULL, NULL, 0);
        err = f2fs_reserve_block(&dn, page->index);
-       f2fs_unlock_op(sbi);
-       if (err)
+       if (err) {
+               f2fs_unlock_op(sbi);
                goto out;
+       }
+       f2fs_put_dnode(&dn);
+       f2fs_unlock_op(sbi);
 
        file_update_time(vma->vm_file);
        lock_page(page);
@@ -296,7 +296,7 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
                goto fail;
 
        /* handle inline data case */
-       if (f2fs_has_inline_data(inode)) {
+       if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) {
                if (whence == SEEK_HOLE)
                        data_ofs = isize;
                goto found;
@@ -374,6 +374,15 @@ static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence)
 
 static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
 {
+       struct inode *inode = file_inode(file);
+
+       /* we don't need to use inline_data strictly */
+       if (f2fs_has_inline_data(inode)) {
+               int err = f2fs_convert_inline_inode(inode);
+               if (err)
+                       return err;
+       }
+
        file_accessed(file);
        vma->vm_ops = &f2fs_file_vm_ops;
        return 0;
@@ -415,20 +424,17 @@ void truncate_data_blocks(struct dnode_of_data *dn)
        truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
 }
 
-static void truncate_partial_data_page(struct inode *inode, u64 from)
+static int truncate_partial_data_page(struct inode *inode, u64 from)
 {
        unsigned offset = from & (PAGE_CACHE_SIZE - 1);
        struct page *page;
 
-       if (f2fs_has_inline_data(inode))
-               return truncate_inline_data(inode, from);
-
        if (!offset)
-               return;
+               return 0;
 
        page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false);
        if (IS_ERR(page))
-               return;
+               return 0;
 
        lock_page(page);
        if (unlikely(!PageUptodate(page) ||
@@ -438,9 +444,9 @@ static void truncate_partial_data_page(struct inode *inode, u64 from)
        f2fs_wait_on_page_writeback(page, DATA);
        zero_user(page, offset, PAGE_CACHE_SIZE - offset);
        set_page_dirty(page);
-
 out:
        f2fs_put_page(page, 1);
+       return 0;
 }
 
 int truncate_blocks(struct inode *inode, u64 from, bool lock)
@@ -450,27 +456,35 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
        struct dnode_of_data dn;
        pgoff_t free_from;
        int count = 0, err = 0;
+       struct page *ipage;
 
        trace_f2fs_truncate_blocks_enter(inode, from);
 
-       if (f2fs_has_inline_data(inode))
-               goto done;
-
        free_from = (pgoff_t)
-                       ((from + blocksize - 1) >> (sbi->log_blocksize));
+               ((from + blocksize - 1) >> (sbi->log_blocksize));
 
        if (lock)
                f2fs_lock_op(sbi);
 
-       set_new_dnode(&dn, inode, NULL, NULL, 0);
+       ipage = get_node_page(sbi, inode->i_ino);
+       if (IS_ERR(ipage)) {
+               err = PTR_ERR(ipage);
+               goto out;
+       }
+
+       if (f2fs_has_inline_data(inode)) {
+               truncate_inline_data(ipage, from);
+               update_inode(inode, ipage);
+               f2fs_put_page(ipage, 1);
+               goto out;
+       }
+
+       set_new_dnode(&dn, inode, ipage, NULL, 0);
        err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE);
        if (err) {
                if (err == -ENOENT)
                        goto free_next;
-               if (lock)
-                       f2fs_unlock_op(sbi);
-               trace_f2fs_truncate_blocks_exit(inode, err);
-               return err;
+               goto out;
        }
 
        count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
@@ -486,11 +500,13 @@ int truncate_blocks(struct inode *inode, u64 from, bool lock)
        f2fs_put_dnode(&dn);
 free_next:
        err = truncate_inode_blocks(inode, free_from);
+
+       /* lastly zero out the first data page */
+       if (!err)
+               err = truncate_partial_data_page(inode, from);
+out:
        if (lock)
                f2fs_unlock_op(sbi);
-done:
-       /* lastly zero out the first data page */
-       truncate_partial_data_page(inode, from);
 
        trace_f2fs_truncate_blocks_exit(inode, err);
        return err;
@@ -561,10 +577,6 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
                return err;
 
        if (attr->ia_valid & ATTR_SIZE) {
-               err = f2fs_convert_inline_data(inode, attr->ia_size, NULL);
-               if (err)
-                       return err;
-
                if (attr->ia_size != i_size_read(inode)) {
                        truncate_setsize(inode, attr->ia_size);
                        f2fs_truncate(inode);
@@ -665,9 +677,11 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
        if (offset >= inode->i_size)
                return ret;
 
-       ret = f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, NULL);
-       if (ret)
-               return ret;
+       if (f2fs_has_inline_data(inode)) {
+               ret = f2fs_convert_inline_inode(inode);
+               if (ret)
+                       return ret;
+       }
 
        pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
        pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
@@ -721,9 +735,11 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
        if (ret)
                return ret;
 
-       ret = f2fs_convert_inline_data(inode, offset + len, NULL);
-       if (ret)
-               return ret;
+       if (f2fs_has_inline_data(inode)) {
+               ret = f2fs_convert_inline_inode(inode);
+               if (ret)
+                       return ret;
+       }
 
        pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
        pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
@@ -874,7 +890,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
 
        set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
 
-       return f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1, NULL);
+       return f2fs_convert_inline_inode(inode);
 }
 
 static int f2fs_ioc_commit_atomic_write(struct file *filp)
@@ -908,7 +924,8 @@ static int f2fs_ioc_start_volatile_write(struct file *filp)
                return -EACCES;
 
        set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
-       return 0;
+
+       return f2fs_convert_inline_inode(inode);
 }
 
 static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)