@@ -4540,40 +4540,15 @@ static long ext4_zero_range(struct file *file, loff_t offset,
struct inode *inode = file_inode(file);
struct address_space *mapping = file->f_mapping;
handle_t *handle = NULL;
- unsigned int max_blocks;
loff_t new_size = 0;
- int ret = 0;
- int flags;
- int credits;
- int partial_begin, partial_end;
- loff_t start, end;
- ext4_lblk_t lblk;
+ loff_t end = offset + len;
+ ext4_lblk_t start_lblk, end_lblk;
+ unsigned int blocksize = i_blocksize(inode);
unsigned int blkbits = inode->i_blkbits;
+ int ret, flags, credits;
trace_ext4_zero_range(inode, offset, len, mode);
- /*
- * Round up offset. This is not fallocate, we need to zero out
- * blocks, so convert interior block aligned part of the range to
- * unwritten and possibly manually zero out unaligned parts of the
- * range. Here, start and partial_begin are inclusive, end and
- * partial_end are exclusive.
- */
- start = round_up(offset, 1 << blkbits);
- end = round_down((offset + len), 1 << blkbits);
-
- if (start < offset || end > offset + len)
- return -EINVAL;
- partial_begin = offset & ((1 << blkbits) - 1);
- partial_end = (offset + len) & ((1 << blkbits) - 1);
-
- lblk = start >> blkbits;
- max_blocks = (end >> blkbits);
- if (max_blocks < lblk)
- max_blocks = 0;
- else
- max_blocks -= lblk;
-
inode_lock(inode);
/*
@@ -4581,26 +4556,23 @@ static long ext4_zero_range(struct file *file, loff_t offset,
*/
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
ret = -EOPNOTSUPP;
- goto out_mutex;
+ goto out;
}
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
- (offset + len > inode->i_size ||
- offset + len > EXT4_I(inode)->i_disksize)) {
- new_size = offset + len;
+ (end > inode->i_size || end > EXT4_I(inode)->i_disksize)) {
+ new_size = end;
ret = inode_newsize_ok(inode, new_size);
if (ret)
- goto out_mutex;
+ goto out;
}
- flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;
-
/* Wait all existing dio workers, newcomers will block on i_rwsem */
inode_dio_wait(inode);
ret = file_modified(file);
if (ret)
- goto out_mutex;
+ goto out;
/*
* Prevent page faults from reinstantiating pages we have released
@@ -4616,36 +4588,40 @@ static long ext4_zero_range(struct file *file, loff_t offset,
* Write data that will be zeroed to preserve them when successfully
* discarding page cache below but fail to convert extents.
*/
- ret = filemap_write_and_wait_range(mapping, start, end - 1);
+ ret = filemap_write_and_wait_range(mapping, offset, end - 1);
if (ret)
goto out_invalidate_lock;
+ /* Now release the pages and zero block aligned part of pages */
+ truncate_pagecache_range(inode, offset, end - 1);
+
+ flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;
/* Preallocate the range including the unaligned edges */
- if (partial_begin || partial_end) {
- ret = ext4_alloc_file_blocks(file,
- round_down(offset, 1 << blkbits) >> blkbits,
- (round_up((offset + len), 1 << blkbits) -
- round_down(offset, 1 << blkbits)) >> blkbits,
- new_size, flags);
+ if (offset & (blocksize - 1) || end & (blocksize - 1)) {
+ ext4_lblk_t alloc_lblk = offset >> blkbits;
+ ext4_lblk_t len_lblk = EXT4_MAX_BLOCKS(len, offset, blkbits);
+
+ ret = ext4_alloc_file_blocks(file, alloc_lblk, len_lblk,
+ new_size, flags);
if (ret)
goto out_invalidate_lock;
}
/* Zero range excluding the unaligned edges */
- if (max_blocks > 0) {
- flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
- EXT4_EX_NOCACHE);
-
- /* Now release the pages and zero block aligned part of pages */
- truncate_pagecache_range(inode, start, end - 1);
-
- ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
- flags);
+ start_lblk = round_up(offset, blocksize) >> blkbits;
+ end_lblk = end >> blkbits;
+ if (end_lblk > start_lblk) {
+ ext4_lblk_t zero_blks = end_lblk - start_lblk;
+
+ flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN | EXT4_EX_NOCACHE);
+ ret = ext4_alloc_file_blocks(file, start_lblk, zero_blks,
+ new_size, flags);
if (ret)
goto out_invalidate_lock;
}
- if (!partial_begin && !partial_end)
+ /* Finish zeroing out if it doesn't contain partial block */
+ if (!(offset & (blocksize - 1)) && !(end & (blocksize - 1)))
goto out_invalidate_lock;
/*
@@ -4662,16 +4638,18 @@ static long ext4_zero_range(struct file *file, loff_t offset,
goto out_invalidate_lock;
}
+ /* Zero out partial block at the edges of the range */
+ ret = ext4_zero_partial_blocks(handle, inode, offset, len);
+ if (ret)
+ goto out_handle;
+
if (new_size)
ext4_update_inode_size(inode, new_size);
ret = ext4_mark_inode_dirty(handle, inode);
if (unlikely(ret))
goto out_handle;
- /* Zero out partial block at the edges of the range */
- ret = ext4_zero_partial_blocks(handle, inode, offset, len);
- if (ret >= 0)
- ext4_update_inode_fsync_trans(handle, inode, 1);
+ ext4_update_inode_fsync_trans(handle, inode, 1);
if (file->f_flags & O_SYNC)
ext4_handle_sync(handle);
@@ -4679,7 +4657,7 @@ static long ext4_zero_range(struct file *file, loff_t offset,
ext4_journal_stop(handle);
out_invalidate_lock:
filemap_invalidate_unlock(mapping);
-out_mutex:
+out:
inode_unlock(inode);
return ret;
}