Skip to content

Commit

Permalink
Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/kernel/git/tytso/ext4

Pull ext4 fixes from Ted Ts'o:
 "These are regression and bug fixes for ext4.

  We had a number of new features in ext4 during this merge window
  (ZERO_RANGE and COLLAPSE_RANGE fallocate modes, renameat, etc.) so
  there were many more regression and bug fixes this time around.  It
  didn't help that xfstests hadn't been fully updated to fully stress
  test COLLAPSE_RANGE until after -rc1"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (31 commits)
  ext4: disable COLLAPSE_RANGE for bigalloc
  ext4: fix COLLAPSE_RANGE failure with 1KB block size
  ext4: use EINVAL if not a regular file in ext4_collapse_range()
  ext4: enforce we are operating on a regular file in ext4_zero_range()
  ext4: fix extent merging in ext4_ext_shift_path_extents()
  ext4: discard preallocations after removing space
  ext4: no need to truncate pagecache twice in collapse range
  ext4: fix removing status extents in ext4_collapse_range()
  ext4: use filemap_write_and_wait_range() correctly in collapse range
  ext4: use truncate_pagecache() in collapse range
  ext4: remove temporary shim used to merge COLLAPSE_RANGE and ZERO_RANGE
  ext4: fix ext4_count_free_clusters() with EXT4FS_DEBUG and bigalloc enabled
  ext4: always check ext4_ext_find_extent result
  ext4: fix error handling in ext4_ext_shift_extents
  ext4: silence sparse check warning for function ext4_trim_extent
  ext4: COLLAPSE_RANGE only works on extent-based files
  ext4: fix byte order problems introduced by the COLLAPSE_RANGE patches
  ext4: use i_size_read in ext4_unaligned_aio()
  fs: disallow all fallocate operation on active swapfile
  fs: move falloc collapse range check into the filesystem methods
  ...
  • Loading branch information
Linus Torvalds committed Apr 21, 2014
2 parents a798c10 + 0a04b24 commit 9ac0367
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 137 deletions.
3 changes: 0 additions & 3 deletions fs/ceph/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1221,9 +1221,6 @@ static long ceph_fallocate(struct file *file, int mode,
if (!S_ISREG(inode->i_mode))
return -EOPNOTSUPP;

if (IS_SWAPFILE(inode))
return -ETXTBSY;

mutex_lock(&inode->i_mutex);

if (ceph_snap(inode) != CEPH_NOSNAP) {
Expand Down
2 changes: 1 addition & 1 deletion fs/ext4/balloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ ext4_fsblk_t ext4_count_free_clusters(struct super_block *sb)
continue;

x = ext4_count_free(bitmap_bh->b_data,
EXT4_BLOCKS_PER_GROUP(sb) / 8);
EXT4_CLUSTERS_PER_GROUP(sb) / 8);
printk(KERN_DEBUG "group %u: stored = %d, counted = %u\n",
i, ext4_free_group_clusters(sb, gdp), x);
bitmap_count += x;
Expand Down
17 changes: 0 additions & 17 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -2466,23 +2466,6 @@ static inline void ext4_update_i_disksize(struct inode *inode, loff_t newsize)
up_write(&EXT4_I(inode)->i_data_sem);
}

/*
* Update i_disksize after writeback has been started. Races with truncate
* are avoided by checking i_size under i_data_sem.
*/
static inline void ext4_wb_update_i_disksize(struct inode *inode, loff_t newsize)
{
loff_t i_size;

down_write(&EXT4_I(inode)->i_data_sem);
i_size = i_size_read(inode);
if (newsize > i_size)
newsize = i_size;
if (newsize > EXT4_I(inode)->i_disksize)
EXT4_I(inode)->i_disksize = newsize;
up_write(&EXT4_I(inode)->i_data_sem);
}

struct ext4_group_info {
unsigned long bb_state;
struct rb_root bb_free_root;
Expand Down
109 changes: 76 additions & 33 deletions fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -3313,6 +3313,11 @@ static int ext4_split_extent(handle_t *handle,
return PTR_ERR(path);
depth = ext_depth(inode);
ex = path[depth].p_ext;
if (!ex) {
EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
(unsigned long) map->m_lblk);
return -EIO;
}
uninitialized = ext4_ext_is_uninitialized(ex);
split_flag1 = 0;

Expand Down Expand Up @@ -3694,6 +3699,12 @@ static int ext4_convert_initialized_extents(handle_t *handle,
}
depth = ext_depth(inode);
ex = path[depth].p_ext;
if (!ex) {
EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
(unsigned long) map->m_lblk);
err = -EIO;
goto out;
}
}

err = ext4_ext_get_access(handle, inode, path + depth);
Expand Down Expand Up @@ -4730,6 +4741,9 @@ static long ext4_zero_range(struct file *file, loff_t offset,

trace_ext4_zero_range(inode, offset, len, mode);

if (!S_ISREG(inode->i_mode))
return -EINVAL;

/*
* Write out all dirty pages to avoid race conditions
* Then release them.
Expand Down Expand Up @@ -4878,9 +4892,6 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
if (mode & FALLOC_FL_PUNCH_HOLE)
return ext4_punch_hole(inode, offset, len);

if (mode & FALLOC_FL_COLLAPSE_RANGE)
return ext4_collapse_range(inode, offset, len);

ret = ext4_convert_inline_data(inode);
if (ret)
return ret;
Expand All @@ -4892,6 +4903,9 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
return -EOPNOTSUPP;

if (mode & FALLOC_FL_COLLAPSE_RANGE)
return ext4_collapse_range(inode, offset, len);

if (mode & FALLOC_FL_ZERO_RANGE)
return ext4_zero_range(file, offset, len, mode);

Expand Down Expand Up @@ -5229,18 +5243,19 @@ 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 = ex_last->ee_block +
*start = le32_to_cpu(ex_last->ee_block) +
ext4_ext_get_actual_len(ex_last);

while (ex_start <= ex_last) {
ex_start->ee_block -= shift;
if (ex_start >
EXT_FIRST_EXTENT(path[depth].p_hdr)) {
if (ext4_ext_try_to_merge_right(inode,
path, ex_start - 1))
ex_last--;
}
ex_start++;
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++;
}
err = ext4_ext_dirty(handle, inode, path + depth);
if (err)
Expand All @@ -5255,7 +5270,7 @@ ext4_ext_shift_path_extents(struct ext4_ext_path *path, ext4_lblk_t shift,
if (err)
goto out;

path[depth].p_idx->ei_block -= shift;
le32_add_cpu(&path[depth].p_idx->ei_block, -shift);
err = ext4_ext_dirty(handle, inode, path + depth);
if (err)
goto out;
Expand Down Expand Up @@ -5300,7 +5315,8 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
return ret;
}

stop_block = extent->ee_block + ext4_ext_get_actual_len(extent);
stop_block = le32_to_cpu(extent->ee_block) +
ext4_ext_get_actual_len(extent);
ext4_ext_drop_refs(path);
kfree(path);

Expand All @@ -5313,10 +5329,18 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
* enough to accomodate the shift.
*/
path = ext4_ext_find_extent(inode, start - 1, NULL, 0);
if (IS_ERR(path))
return PTR_ERR(path);
depth = path->p_depth;
extent = path[depth].p_ext;
ex_start = extent->ee_block;
ex_end = extent->ee_block + ext4_ext_get_actual_len(extent);
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;
}
ext4_ext_drop_refs(path);
kfree(path);

Expand All @@ -5331,7 +5355,13 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
return PTR_ERR(path);
depth = path->p_depth;
extent = path[depth].p_ext;
current_block = extent->ee_block;
if (!extent) {
EXT4_ERROR_INODE(inode, "unexpected hole at %lu",
(unsigned long) start);
return -EIO;
}

current_block = le32_to_cpu(extent->ee_block);
if (start > current_block) {
/* Hole, move to the next extent */
ret = mext_next_extent(inode, path, &extent);
Expand Down Expand Up @@ -5365,40 +5395,53 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
ext4_lblk_t punch_start, punch_stop;
handle_t *handle;
unsigned int credits;
loff_t new_size;
loff_t new_size, ioffset;
int ret;

BUG_ON(offset + len > i_size_read(inode));

/* Collapse range works only on fs block size aligned offsets. */
if (offset & (EXT4_BLOCK_SIZE(sb) - 1) ||
len & (EXT4_BLOCK_SIZE(sb) - 1))
return -EINVAL;

if (!S_ISREG(inode->i_mode))
return -EINVAL;

if (EXT4_SB(inode->i_sb)->s_cluster_ratio > 1)
return -EOPNOTSUPP;

trace_ext4_collapse_range(inode, offset, len);

punch_start = offset >> EXT4_BLOCK_SIZE_BITS(sb);
punch_stop = (offset + 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 offset to be aligned with 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, offset, -1);
ret = filemap_write_and_wait_range(inode->i_mapping, ioffset,
LLONG_MAX);
if (ret)
return ret;

/* Take mutex lock */
mutex_lock(&inode->i_mutex);

/* It's not possible punch hole on append only file */
if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) {
ret = -EPERM;
goto out_mutex;
}

if (IS_SWAPFILE(inode)) {
ret = -ETXTBSY;
/*
* There is no need to overlap collapse range with EOF, in which case
* it is effectively a truncate operation
*/
if (offset + len >= i_size_read(inode)) {
ret = -EINVAL;
goto out_mutex;
}

Expand All @@ -5408,7 +5451,7 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
goto out_mutex;
}

truncate_pagecache_range(inode, offset, -1);
truncate_pagecache(inode, ioffset);

/* Wait for existing dio to complete */
ext4_inode_block_unlocked_dio(inode);
Expand All @@ -5425,7 +5468,7 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
ext4_discard_preallocations(inode);

ret = ext4_es_remove_extent(inode, punch_start,
EXT_MAX_BLOCKS - punch_start - 1);
EXT_MAX_BLOCKS - punch_start);
if (ret) {
up_write(&EXT4_I(inode)->i_data_sem);
goto out_stop;
Expand All @@ -5436,6 +5479,7 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
up_write(&EXT4_I(inode)->i_data_sem);
goto out_stop;
}
ext4_discard_preallocations(inode);

ret = ext4_ext_shift_extents(inode, handle, punch_stop,
punch_stop - punch_start);
Expand All @@ -5445,10 +5489,9 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
}

new_size = i_size_read(inode) - len;
truncate_setsize(inode, new_size);
i_size_write(inode, new_size);
EXT4_I(inode)->i_disksize = new_size;

ext4_discard_preallocations(inode);
up_write(&EXT4_I(inode)->i_data_sem);
if (IS_SYNC(inode))
ext4_handle_sync(handle);
Expand Down
2 changes: 1 addition & 1 deletion fs/ext4/extents_status.c
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,7 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk,

newes.es_lblk = end + 1;
newes.es_len = len2;
block = 0x7FDEADBEEF;
block = 0x7FDEADBEEFULL;
if (ext4_es_is_written(&orig_es) ||
ext4_es_is_unwritten(&orig_es))
block = ext4_es_pblock(&orig_es) +
Expand Down
2 changes: 1 addition & 1 deletion fs/ext4/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ ext4_unaligned_aio(struct inode *inode, const struct iovec *iov,
size_t count = iov_length(iov, nr_segs);
loff_t final_size = pos + count;

if (pos >= inode->i_size)
if (pos >= i_size_read(inode))
return 0;

if ((pos & blockmask) || (final_size & blockmask))
Expand Down
Loading

0 comments on commit 9ac0367

Please sign in to comment.