Merge branch 'for-4.2/sg' of git://git.kernel.dk/linux-block
[firefly-linux-kernel-4.4.55.git] / fs / ext4 / extents.c
index e003a1e81dc351c76465908bec21702ecc6d4b91..d86d2622f82631f3fa46abe1047caf319b968402 100644 (file)
@@ -4456,6 +4456,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
                ar.flags |= EXT4_MB_HINT_NOPREALLOC;
        if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)
                ar.flags |= EXT4_MB_DELALLOC_RESERVED;
+       if (flags & EXT4_GET_BLOCKS_METADATA_NOFAIL)
+               ar.flags |= EXT4_MB_USE_RESERVED;
        newblock = ext4_mb_new_blocks(handle, &ar, &err);
        if (!newblock)
                goto out2;
@@ -4663,6 +4665,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
        int ret = 0;
        int ret2 = 0;
        int retries = 0;
+       int depth = 0;
        struct ext4_map_blocks map;
        unsigned int credits;
        loff_t epos;
@@ -4677,13 +4680,32 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
        if (len <= EXT_UNWRITTEN_MAX_LEN)
                flags |= EXT4_GET_BLOCKS_NO_NORMALIZE;
 
+       /* Wait all existing dio workers, newcomers will block on i_mutex */
+       ext4_inode_block_unlocked_dio(inode);
+       inode_dio_wait(inode);
+
        /*
         * credits to insert 1 extent into extent tree
         */
        credits = ext4_chunk_trans_blocks(inode, len);
+       /*
+        * We can only call ext_depth() on extent based inodes
+        */
+       if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
+               depth = ext_depth(inode);
+       else
+               depth = -1;
 
 retry:
        while (ret >= 0 && len) {
+               /*
+                * Recalculate credits when extent tree depth changes.
+                */
+               if (depth >= 0 && depth != ext_depth(inode)) {
+                       credits = ext4_chunk_trans_blocks(inode, len);
+                       depth = ext_depth(inode);
+               }
+
                handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS,
                                            credits);
                if (IS_ERR(handle)) {
@@ -4725,6 +4747,8 @@ retry:
                goto retry;
        }
 
+       ext4_inode_resume_unlocked_dio(inode);
+
        return ret > 0 ? ret2 : ret;
 }
 
@@ -4912,12 +4936,14 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
         * bug we should fix....
         */
        if (ext4_encrypted_inode(inode) &&
-           (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)))
+           (mode & (FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_INSERT_RANGE |
+                    FALLOC_FL_ZERO_RANGE)))
                return -EOPNOTSUPP;
 
        /* Return error if mode is not supported */
        if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
-                    FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
+                    FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |
+                    FALLOC_FL_INSERT_RANGE))
                return -EOPNOTSUPP;
 
        if (mode & FALLOC_FL_PUNCH_HOLE)
@@ -4930,6 +4956,9 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
        if (mode & FALLOC_FL_COLLAPSE_RANGE)
                return ext4_collapse_range(inode, offset, len);
 
+       if (mode & FALLOC_FL_INSERT_RANGE)
+               return ext4_insert_range(inode, offset, len);
+
        if (mode & FALLOC_FL_ZERO_RANGE)
                return ext4_zero_range(file, offset, len, mode);
 
@@ -5224,13 +5253,13 @@ ext4_access_path(handle_t *handle, struct inode *inode,
 /*
  * ext4_ext_shift_path_extents:
  * Shift the extents of a path structure lying between path[depth].p_ext
- * and EXT_LAST_EXTENT(path[depth].p_hdr) downwards, by subtracting shift
- * from starting block for each extent.
+ * and EXT_LAST_EXTENT(path[depth].p_hdr), by @shift blocks. @SHIFT tells
+ * if it is right shift or left shift operation.
  */
 static int
 ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
                            struct inode *inode, handle_t *handle,
-                           ext4_lblk_t *start)
+                           enum SHIFT_DIRECTION SHIFT)
 {
        int depth, err = 0;
        struct ext4_extent *ex_start, *ex_last;
@@ -5252,19 +5281,25 @@ ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
                        if (ex_start == EXT_FIRST_EXTENT(path[depth].p_hdr))
                                update = 1;
 
-                       *start = le32_to_cpu(ex_last->ee_block) +
-                               ext4_ext_get_actual_len(ex_last);
-
                        while (ex_start <= ex_last) {
-                               le32_add_cpu(&ex_start->ee_block, -shift);
-                               /* Try to merge to the left. */
-                               if ((ex_start >
-                                    EXT_FIRST_EXTENT(path[depth].p_hdr)) &&
-                                   ext4_ext_try_to_merge_right(inode,
-                                                       path, ex_start - 1))
+                               if (SHIFT == SHIFT_LEFT) {
+                                       le32_add_cpu(&ex_start->ee_block,
+                                               -shift);
+                                       /* Try to merge to the left. */
+                                       if ((ex_start >
+                                           EXT_FIRST_EXTENT(path[depth].p_hdr))
+                                           &&
+                                           ext4_ext_try_to_merge_right(inode,
+                                           path, ex_start - 1))
+                                               ex_last--;
+                                       else
+                                               ex_start++;
+                               } else {
+                                       le32_add_cpu(&ex_last->ee_block, shift);
+                                       ext4_ext_try_to_merge_right(inode, path,
+                                               ex_last);
                                        ex_last--;
-                               else
-                                       ex_start++;
+                               }
                        }
                        err = ext4_ext_dirty(handle, inode, path + depth);
                        if (err)
@@ -5279,7 +5314,10 @@ ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
                if (err)
                        goto out;
 
-               le32_add_cpu(&path[depth].p_idx->ei_block, -shift);
+               if (SHIFT == SHIFT_LEFT)
+                       le32_add_cpu(&path[depth].p_idx->ei_block, -shift);
+               else
+                       le32_add_cpu(&path[depth].p_idx->ei_block, shift);
                err = ext4_ext_dirty(handle, inode, path + depth);
                if (err)
                        goto out;
@@ -5297,19 +5335,20 @@ out:
 
 /*
  * ext4_ext_shift_extents:
- * All the extents which lies in the range from start to the last allocated
- * block for the file are shifted downwards by shift blocks.
+ * All the extents which lies in the range from @start to the last allocated
+ * block for the @inode are shifted either towards left or right (depending
+ * upon @SHIFT) by @shift blocks.
  * On success, 0 is returned, error otherwise.
  */
 static int
 ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
-                      ext4_lblk_t start, ext4_lblk_t shift)
+                      ext4_lblk_t start, ext4_lblk_t shift,
+                      enum SHIFT_DIRECTION SHIFT)
 {
        struct ext4_ext_path *path;
        int ret = 0, depth;
        struct ext4_extent *extent;
-       ext4_lblk_t stop_block;
-       ext4_lblk_t ex_start, ex_end;
+       ext4_lblk_t stop, *iterator, ex_start, ex_end;
 
        /* Let path point to the last extent */
        path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0);
@@ -5321,58 +5360,84 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
        if (!extent)
                goto out;
 
-       stop_block = le32_to_cpu(extent->ee_block) +
+       stop = le32_to_cpu(extent->ee_block) +
                        ext4_ext_get_actual_len(extent);
 
-       /* Nothing to shift, if hole is at the end of file */
-       if (start >= stop_block)
-               goto out;
+       /*
+        * In case of left shift, Don't start shifting extents until we make
+        * sure the hole is big enough to accommodate the shift.
+       */
+       if (SHIFT == SHIFT_LEFT) {
+               path = ext4_find_extent(inode, start - 1, &path, 0);
+               if (IS_ERR(path))
+                       return PTR_ERR(path);
+               depth = path->p_depth;
+               extent =  path[depth].p_ext;
+               if (extent) {
+                       ex_start = le32_to_cpu(extent->ee_block);
+                       ex_end = le32_to_cpu(extent->ee_block) +
+                               ext4_ext_get_actual_len(extent);
+               } else {
+                       ex_start = 0;
+                       ex_end = 0;
+               }
 
-       /*
-        * Don't start shifting extents until we make sure the hole is big
-        * enough to accomodate the shift.
-        */
-       path = ext4_find_extent(inode, start - 1, &path, 0);
-       if (IS_ERR(path))
-               return PTR_ERR(path);
-       depth = path->p_depth;
-       extent =  path[depth].p_ext;
-       if (extent) {
-               ex_start = le32_to_cpu(extent->ee_block);
-               ex_end = le32_to_cpu(extent->ee_block) +
-                       ext4_ext_get_actual_len(extent);
-       } else {
-               ex_start = 0;
-               ex_end = 0;
+               if ((start == ex_start && shift > ex_start) ||
+                   (shift > start - ex_end)) {
+                       ext4_ext_drop_refs(path);
+                       kfree(path);
+                       return -EINVAL;
+               }
        }
 
-       if ((start == ex_start && shift > ex_start) ||
-           (shift > start - ex_end))
-               return -EINVAL;
+       /*
+        * In case of left shift, iterator points to start and it is increased
+        * till we reach stop. In case of right shift, iterator points to stop
+        * and it is decreased till we reach start.
+        */
+       if (SHIFT == SHIFT_LEFT)
+               iterator = &start;
+       else
+               iterator = &stop;
 
        /* Its safe to start updating extents */
-       while (start < stop_block) {
-               path = ext4_find_extent(inode, start, &path, 0);
+       while (start < stop) {
+               path = ext4_find_extent(inode, *iterator, &path, 0);
                if (IS_ERR(path))
                        return PTR_ERR(path);
                depth = path->p_depth;
                extent = path[depth].p_ext;
                if (!extent) {
                        EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
-                                        (unsigned long) start);
+                                        (unsigned long) *iterator);
                        return -EIO;
                }
-               if (start > le32_to_cpu(extent->ee_block)) {
+               if (SHIFT == SHIFT_LEFT && *iterator >
+                   le32_to_cpu(extent->ee_block)) {
                        /* Hole, move to the next extent */
                        if (extent < EXT_LAST_EXTENT(path[depth].p_hdr)) {
                                path[depth].p_ext++;
                        } else {
-                               start = ext4_ext_next_allocated_block(path);
+                               *iterator = ext4_ext_next_allocated_block(path);
                                continue;
                        }
                }
+
+               if (SHIFT == SHIFT_LEFT) {
+                       extent = EXT_LAST_EXTENT(path[depth].p_hdr);
+                       *iterator = le32_to_cpu(extent->ee_block) +
+                                       ext4_ext_get_actual_len(extent);
+               } else {
+                       extent = EXT_FIRST_EXTENT(path[depth].p_hdr);
+                       *iterator =  le32_to_cpu(extent->ee_block) > 0 ?
+                               le32_to_cpu(extent->ee_block) - 1 : 0;
+                       /* Update path extent in case we need to stop */
+                       while (le32_to_cpu(extent->ee_block) < start)
+                               extent++;
+                       path[depth].p_ext = extent;
+               }
                ret = ext4_ext_shift_path_extents(path, shift, inode,
-                               handle, &start);
+                               handle, SHIFT);
                if (ret)
                        break;
        }
@@ -5485,7 +5550,7 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
        ext4_discard_preallocations(inode);
 
        ret = ext4_ext_shift_extents(inode, handle, punch_stop,
-                                    punch_stop - punch_start);
+                                    punch_stop - punch_start, SHIFT_LEFT);
        if (ret) {
                up_write(&EXT4_I(inode)->i_data_sem);
                goto out_stop;
@@ -5510,6 +5575,174 @@ out_mutex:
        return ret;
 }
 
+/*
+ * ext4_insert_range:
+ * This function implements the FALLOC_FL_INSERT_RANGE flag of fallocate.
+ * The data blocks starting from @offset to the EOF are shifted by @len
+ * towards right to create a hole in the @inode. Inode size is increased
+ * by len bytes.
+ * Returns 0 on success, error otherwise.
+ */
+int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
+{
+       struct super_block *sb = inode->i_sb;
+       handle_t *handle;
+       struct ext4_ext_path *path;
+       struct ext4_extent *extent;
+       ext4_lblk_t offset_lblk, len_lblk, ee_start_lblk = 0;
+       unsigned int credits, ee_len;
+       int ret = 0, depth, split_flag = 0;
+       loff_t ioffset;
+
+       /*
+        * We need to test this early because xfstests assumes that an
+        * insert range of (0, 1) will return EOPNOTSUPP if the file
+        * system does not support insert range.
+        */
+       if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
+               return -EOPNOTSUPP;
+
+       /* Insert range works only on fs block size aligned offsets. */
+       if (offset & (EXT4_CLUSTER_SIZE(sb) - 1) ||
+                       len & (EXT4_CLUSTER_SIZE(sb) - 1))
+               return -EINVAL;
+
+       if (!S_ISREG(inode->i_mode))
+               return -EOPNOTSUPP;
+
+       trace_ext4_insert_range(inode, offset, len);
+
+       offset_lblk = offset >> EXT4_BLOCK_SIZE_BITS(sb);
+       len_lblk = len >> EXT4_BLOCK_SIZE_BITS(sb);
+
+       /* Call ext4_force_commit to flush all data in case of data=journal */
+       if (ext4_should_journal_data(inode)) {
+               ret = ext4_force_commit(inode->i_sb);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * Need to round down to align start offset to page size boundary
+        * for page size > block size.
+        */
+       ioffset = round_down(offset, PAGE_SIZE);
+
+       /* Write out all dirty pages */
+       ret = filemap_write_and_wait_range(inode->i_mapping, ioffset,
+                       LLONG_MAX);
+       if (ret)
+               return ret;
+
+       /* Take mutex lock */
+       mutex_lock(&inode->i_mutex);
+
+       /* Currently just for extent based files */
+       if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+               ret = -EOPNOTSUPP;
+               goto out_mutex;
+       }
+
+       /* Check for wrap through zero */
+       if (inode->i_size + len > inode->i_sb->s_maxbytes) {
+               ret = -EFBIG;
+               goto out_mutex;
+       }
+
+       /* Offset should be less than i_size */
+       if (offset >= i_size_read(inode)) {
+               ret = -EINVAL;
+               goto out_mutex;
+       }
+
+       truncate_pagecache(inode, ioffset);
+
+       /* Wait for existing dio to complete */
+       ext4_inode_block_unlocked_dio(inode);
+       inode_dio_wait(inode);
+
+       credits = ext4_writepage_trans_blocks(inode);
+       handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               goto out_dio;
+       }
+
+       /* Expand file to avoid data loss if there is error while shifting */
+       inode->i_size += len;
+       EXT4_I(inode)->i_disksize += len;
+       inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
+       ret = ext4_mark_inode_dirty(handle, inode);
+       if (ret)
+               goto out_stop;
+
+       down_write(&EXT4_I(inode)->i_data_sem);
+       ext4_discard_preallocations(inode);
+
+       path = ext4_find_extent(inode, offset_lblk, NULL, 0);
+       if (IS_ERR(path)) {
+               up_write(&EXT4_I(inode)->i_data_sem);
+               goto out_stop;
+       }
+
+       depth = ext_depth(inode);
+       extent = path[depth].p_ext;
+       if (extent) {
+               ee_start_lblk = le32_to_cpu(extent->ee_block);
+               ee_len = ext4_ext_get_actual_len(extent);
+
+               /*
+                * If offset_lblk is not the starting block of extent, split
+                * the extent @offset_lblk
+                */
+               if ((offset_lblk > ee_start_lblk) &&
+                               (offset_lblk < (ee_start_lblk + ee_len))) {
+                       if (ext4_ext_is_unwritten(extent))
+                               split_flag = EXT4_EXT_MARK_UNWRIT1 |
+                                       EXT4_EXT_MARK_UNWRIT2;
+                       ret = ext4_split_extent_at(handle, inode, &path,
+                                       offset_lblk, split_flag,
+                                       EXT4_EX_NOCACHE |
+                                       EXT4_GET_BLOCKS_PRE_IO |
+                                       EXT4_GET_BLOCKS_METADATA_NOFAIL);
+               }
+
+               ext4_ext_drop_refs(path);
+               kfree(path);
+               if (ret < 0) {
+                       up_write(&EXT4_I(inode)->i_data_sem);
+                       goto out_stop;
+               }
+       }
+
+       ret = ext4_es_remove_extent(inode, offset_lblk,
+                       EXT_MAX_BLOCKS - offset_lblk);
+       if (ret) {
+               up_write(&EXT4_I(inode)->i_data_sem);
+               goto out_stop;
+       }
+
+       /*
+        * if offset_lblk lies in a hole which is at start of file, use
+        * ee_start_lblk to shift extents
+        */
+       ret = ext4_ext_shift_extents(inode, handle,
+               ee_start_lblk > offset_lblk ? ee_start_lblk : offset_lblk,
+               len_lblk, SHIFT_RIGHT);
+
+       up_write(&EXT4_I(inode)->i_data_sem);
+       if (IS_SYNC(inode))
+               ext4_handle_sync(handle);
+
+out_stop:
+       ext4_journal_stop(handle);
+out_dio:
+       ext4_inode_resume_unlocked_dio(inode);
+out_mutex:
+       mutex_unlock(&inode->i_mutex);
+       return ret;
+}
+
 /**
  * ext4_swap_extents - Swap extents between two inodes
  *
@@ -5542,7 +5775,7 @@ ext4_swap_extents(handle_t *handle, struct inode *inode1,
        BUG_ON(!rwsem_is_locked(&EXT4_I(inode1)->i_data_sem));
        BUG_ON(!rwsem_is_locked(&EXT4_I(inode2)->i_data_sem));
        BUG_ON(!mutex_is_locked(&inode1->i_mutex));
-       BUG_ON(!mutex_is_locked(&inode1->i_mutex));
+       BUG_ON(!mutex_is_locked(&inode2->i_mutex));
 
        *erp = ext4_es_remove_extent(inode1, lblk1, count);
        if (unlikely(*erp))