Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / fs / f2fs / data.c
index 2c0cb661791807a67569bdf8d544dc0fa8b3cc55..985ed023a750170b924455ea23e2684c50baeba4 100644 (file)
@@ -274,7 +274,7 @@ static int check_extent_cache(struct inode *inode, pgoff_t pgofs,
                unsigned int blkbits = inode->i_sb->s_blocksize_bits;
                size_t count;
 
-               clear_buffer_new(bh_result);
+               set_buffer_new(bh_result);
                map_bh(bh_result, inode->i_sb,
                                start_blkaddr + pgofs - start_fofs);
                count = end_fofs - pgofs + 1;
@@ -299,8 +299,6 @@ void update_extent_cache(struct dnode_of_data *dn)
        int need_update = true;
 
        f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR);
-       fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
-                                                       dn->ofs_in_node;
 
        /* Update the page address in the parent node */
        __set_data_blkaddr(dn);
@@ -308,6 +306,9 @@ void update_extent_cache(struct dnode_of_data *dn)
        if (is_inode_flag_set(fi, FI_NO_EXTENT))
                return;
 
+       fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
+                                                       dn->ofs_in_node;
+
        write_lock(&fi->ext.ext_lock);
 
        start_fofs = fi->ext.fofs;
@@ -562,31 +563,25 @@ static int __allocate_data_block(struct dnode_of_data *dn)
        struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
        struct f2fs_inode_info *fi = F2FS_I(dn->inode);
        struct f2fs_summary sum;
-       block_t new_blkaddr;
        struct node_info ni;
+       int seg = CURSEG_WARM_DATA;
        pgoff_t fofs;
-       int type;
 
        if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC)))
                return -EPERM;
        if (unlikely(!inc_valid_block_count(sbi, dn->inode, 1)))
                return -ENOSPC;
 
-       dn->data_blkaddr = NEW_ADDR;
-       __set_data_blkaddr(dn);
-
        get_node_info(sbi, dn->nid, &ni);
        set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
 
-       type = CURSEG_WARM_DATA;
+       if (dn->ofs_in_node == 0 && dn->inode_page == dn->node_page)
+               seg = CURSEG_DIRECT_IO;
 
-       allocate_data_block(sbi, NULL, NULL_ADDR, &new_blkaddr, &sum, type);
+       allocate_data_block(sbi, NULL, NULL_ADDR, &dn->data_blkaddr, &sum, seg);
 
        /* direct IO doesn't use extent cache to maximize the performance */
-       set_inode_flag(F2FS_I(dn->inode), FI_NO_EXTENT);
-       dn->data_blkaddr = new_blkaddr;
-       update_extent_cache(dn);
-       clear_inode_flag(F2FS_I(dn->inode), FI_NO_EXTENT);
+       __set_data_blkaddr(dn);
 
        /* update i_size */
        fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) +
@@ -594,10 +589,59 @@ static int __allocate_data_block(struct dnode_of_data *dn)
        if (i_size_read(dn->inode) < ((fofs + 1) << PAGE_CACHE_SHIFT))
                i_size_write(dn->inode, ((fofs + 1) << PAGE_CACHE_SHIFT));
 
-       dn->data_blkaddr = new_blkaddr;
        return 0;
 }
 
+static void __allocate_data_blocks(struct inode *inode, loff_t offset,
+                                                       size_t count)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       struct dnode_of_data dn;
+       u64 start = F2FS_BYTES_TO_BLK(offset);
+       u64 len = F2FS_BYTES_TO_BLK(count);
+       bool allocated;
+       u64 end_offset;
+
+       while (len) {
+               f2fs_balance_fs(sbi);
+               f2fs_lock_op(sbi);
+
+               /* When reading holes, we need its node page */
+               set_new_dnode(&dn, inode, NULL, NULL, 0);
+               if (get_dnode_of_data(&dn, start, ALLOC_NODE))
+                       goto out;
+
+               allocated = false;
+               end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
+
+               while (dn.ofs_in_node < end_offset && len) {
+                       if (dn.data_blkaddr == NULL_ADDR) {
+                               if (__allocate_data_block(&dn))
+                                       goto sync_out;
+                               allocated = true;
+                       }
+                       len--;
+                       start++;
+                       dn.ofs_in_node++;
+               }
+
+               if (allocated)
+                       sync_inode_page(&dn);
+
+               f2fs_put_dnode(&dn);
+               f2fs_unlock_op(sbi);
+       }
+       return;
+
+sync_out:
+       if (allocated)
+               sync_inode_page(&dn);
+       f2fs_put_dnode(&dn);
+out:
+       f2fs_unlock_op(sbi);
+       return;
+}
+
 /*
  * get_data_block() now supported readahead/bmap/rw direct_IO with mapped bh.
  * If original data blocks are allocated, then give them to blockdev.
@@ -623,10 +667,8 @@ static int __get_data_block(struct inode *inode, sector_t iblock,
        if (check_extent_cache(inode, pgofs, bh_result))
                goto out;
 
-       if (create) {
-               f2fs_balance_fs(F2FS_I_SB(inode));
+       if (create)
                f2fs_lock_op(F2FS_I_SB(inode));
-       }
 
        /* When reading holes, we need its node page */
        set_new_dnode(&dn, inode, NULL, NULL, 0);
@@ -640,12 +682,14 @@ static int __get_data_block(struct inode *inode, sector_t iblock,
                goto put_out;
 
        if (dn.data_blkaddr != NULL_ADDR) {
+               set_buffer_new(bh_result);
                map_bh(bh_result, inode->i_sb, dn.data_blkaddr);
        } else if (create) {
                err = __allocate_data_block(&dn);
                if (err)
                        goto put_out;
                allocated = true;
+               set_buffer_new(bh_result);
                map_bh(bh_result, inode->i_sb, dn.data_blkaddr);
        } else {
                goto put_out;
@@ -824,7 +868,7 @@ static int f2fs_write_data_page(struct page *page,
 
        zero_user_segment(page, offset, PAGE_CACHE_SIZE);
 write:
-       if (unlikely(sbi->por_doing))
+       if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
                goto redirty_out;
        if (f2fs_is_drop_cache(inode))
                goto out;
@@ -843,7 +887,6 @@ write:
        /* we should bypass data pages to proceed the kworkder jobs */
        if (unlikely(f2fs_cp_error(sbi))) {
                SetPageError(page);
-               unlock_page(page);
                goto out;
        }
 
@@ -1113,6 +1156,9 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
 
        trace_f2fs_direct_IO_enter(inode, offset, count, rw);
 
+       if (rw & WRITE)
+               __allocate_data_blocks(inode, offset, count);
+
        err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block);
        if (err < 0 && (rw & WRITE))
                f2fs_write_failed(mapping, offset + count);
@@ -1122,21 +1168,33 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb,
        return err;
 }
 
-static void f2fs_invalidate_data_page(struct page *page, unsigned int offset,
-                                     unsigned int length)
+void f2fs_invalidate_page(struct page *page, unsigned int offset,
+                                                       unsigned int length)
 {
        struct inode *inode = page->mapping->host;
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
 
-       if (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE)
+       if (inode->i_ino >= F2FS_ROOT_INO(sbi) &&
+               (offset % PAGE_CACHE_SIZE || length != PAGE_CACHE_SIZE))
                return;
 
-       if (PageDirty(page))
-               inode_dec_dirty_pages(inode);
+       if (PageDirty(page)) {
+               if (inode->i_ino == F2FS_META_INO(sbi))
+                       dec_page_count(sbi, F2FS_DIRTY_META);
+               else if (inode->i_ino == F2FS_NODE_INO(sbi))
+                       dec_page_count(sbi, F2FS_DIRTY_NODES);
+               else
+                       inode_dec_dirty_pages(inode);
+       }
        ClearPagePrivate(page);
 }
 
-static int f2fs_release_data_page(struct page *page, gfp_t wait)
+int f2fs_release_page(struct page *page, gfp_t wait)
 {
+       /* If this is dirty page, keep PagePrivate */
+       if (PageDirty(page))
+               return 0;
+
        ClearPagePrivate(page);
        return 1;
 }
@@ -1186,8 +1244,8 @@ const struct address_space_operations f2fs_dblock_aops = {
        .write_begin    = f2fs_write_begin,
        .write_end      = f2fs_write_end,
        .set_page_dirty = f2fs_set_data_page_dirty,
-       .invalidatepage = f2fs_invalidate_data_page,
-       .releasepage    = f2fs_release_data_page,
+       .invalidatepage = f2fs_invalidate_page,
+       .releasepage    = f2fs_release_page,
        .direct_IO      = f2fs_direct_IO,
        .bmap           = f2fs_bmap,
 };