Commit 03bd8b9b authored by Dmitry Monakhov's avatar Dmitry Monakhov Committed by Theodore Ts'o

ext4: move_extent code cleanup

- Remove usless checks, because it is too late to check that inode != NULL
  at the moment it was referenced several times.
- Double lock routines looks very ugly and locking ordering relays on
  order of i_ino, but other kernel code rely on order of pointers.
  Let's make them simple and clean.
- check that inodes belongs to the same SB as soon as possible.
Signed-off-by: default avatarDmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
Cc: stable@vger.kernel.org
parent 0acdb887
...@@ -140,56 +140,22 @@ mext_next_extent(struct inode *inode, struct ext4_ext_path *path, ...@@ -140,56 +140,22 @@ mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
return 1; return 1;
} }
/**
* mext_check_null_inode - NULL check for two inodes
*
* If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0.
*/
static int
mext_check_null_inode(struct inode *inode1, struct inode *inode2,
const char *function, unsigned int line)
{
int ret = 0;
if (inode1 == NULL) {
__ext4_error(inode2->i_sb, function, line,
"Both inodes should not be NULL: "
"inode1 NULL inode2 %lu", inode2->i_ino);
ret = -EIO;
} else if (inode2 == NULL) {
__ext4_error(inode1->i_sb, function, line,
"Both inodes should not be NULL: "
"inode1 %lu inode2 NULL", inode1->i_ino);
ret = -EIO;
}
return ret;
}
/** /**
* double_down_write_data_sem - Acquire two inodes' write lock of i_data_sem * double_down_write_data_sem - Acquire two inodes' write lock of i_data_sem
* *
* @orig_inode: original inode structure * Acquire write lock of i_data_sem of the two inodes
* @donor_inode: donor inode structure
* Acquire write lock of i_data_sem of the two inodes (orig and donor) by
* i_ino order.
*/ */
static void static void
double_down_write_data_sem(struct inode *orig_inode, struct inode *donor_inode) double_down_write_data_sem(struct inode *first, struct inode *second)
{ {
struct inode *first = orig_inode, *second = donor_inode; if (first < second) {
down_write(&EXT4_I(first)->i_data_sem);
down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING);
} else {
down_write(&EXT4_I(second)->i_data_sem);
down_write_nested(&EXT4_I(first)->i_data_sem, SINGLE_DEPTH_NESTING);
/*
* Use the inode number to provide the stable locking order instead
* of its address, because the C language doesn't guarantee you can
* compare pointers that don't come from the same array.
*/
if (donor_inode->i_ino < orig_inode->i_ino) {
first = donor_inode;
second = orig_inode;
} }
down_write(&EXT4_I(first)->i_data_sem);
down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING);
} }
/** /**
...@@ -969,14 +935,6 @@ mext_check_arguments(struct inode *orig_inode, ...@@ -969,14 +935,6 @@ mext_check_arguments(struct inode *orig_inode,
return -EINVAL; return -EINVAL;
} }
/* Files should be in the same ext4 FS */
if (orig_inode->i_sb != donor_inode->i_sb) {
ext4_debug("ext4 move extent: The argument files "
"should be in same FS [ino:orig %lu, donor %lu]\n",
orig_inode->i_ino, donor_inode->i_ino);
return -EINVAL;
}
/* Ext4 move extent supports only extent based file */ /* Ext4 move extent supports only extent based file */
if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) { if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) {
ext4_debug("ext4 move extent: orig file is not extents " ext4_debug("ext4 move extent: orig file is not extents "
...@@ -1072,35 +1030,19 @@ mext_check_arguments(struct inode *orig_inode, ...@@ -1072,35 +1030,19 @@ mext_check_arguments(struct inode *orig_inode,
* @inode1: the inode structure * @inode1: the inode structure
* @inode2: the inode structure * @inode2: the inode structure
* *
* Lock two inodes' i_mutex by i_ino order. * Lock two inodes' i_mutex
* If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0.
*/ */
static int static void
mext_inode_double_lock(struct inode *inode1, struct inode *inode2) mext_inode_double_lock(struct inode *inode1, struct inode *inode2)
{ {
int ret = 0; BUG_ON(inode1 == inode2);
if (inode1 < inode2) {
BUG_ON(inode1 == NULL && inode2 == NULL);
ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__);
if (ret < 0)
goto out;
if (inode1 == inode2) {
mutex_lock(&inode1->i_mutex);
goto out;
}
if (inode1->i_ino < inode2->i_ino) {
mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT);
mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD);
} else { } else {
mutex_lock_nested(&inode2->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&inode2->i_mutex, I_MUTEX_PARENT);
mutex_lock_nested(&inode1->i_mutex, I_MUTEX_CHILD); mutex_lock_nested(&inode1->i_mutex, I_MUTEX_CHILD);
} }
out:
return ret;
} }
/** /**
...@@ -1109,28 +1051,13 @@ mext_inode_double_lock(struct inode *inode1, struct inode *inode2) ...@@ -1109,28 +1051,13 @@ mext_inode_double_lock(struct inode *inode1, struct inode *inode2)
* @inode1: the inode that is released first * @inode1: the inode that is released first
* @inode2: the inode that is released second * @inode2: the inode that is released second
* *
* If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0.
*/ */
static int static void
mext_inode_double_unlock(struct inode *inode1, struct inode *inode2) mext_inode_double_unlock(struct inode *inode1, struct inode *inode2)
{ {
int ret = 0; mutex_unlock(&inode1->i_mutex);
mutex_unlock(&inode2->i_mutex);
BUG_ON(inode1 == NULL && inode2 == NULL);
ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__);
if (ret < 0)
goto out;
if (inode1)
mutex_unlock(&inode1->i_mutex);
if (inode2 && inode2 != inode1)
mutex_unlock(&inode2->i_mutex);
out:
return ret;
} }
/** /**
...@@ -1187,16 +1114,23 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1187,16 +1114,23 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
ext4_lblk_t block_end, seq_start, add_blocks, file_end, seq_blocks = 0; ext4_lblk_t block_end, seq_start, add_blocks, file_end, seq_blocks = 0;
ext4_lblk_t rest_blocks; ext4_lblk_t rest_blocks;
pgoff_t orig_page_offset = 0, seq_end_page; pgoff_t orig_page_offset = 0, seq_end_page;
int ret1, ret2, depth, last_extent = 0; int ret, depth, last_extent = 0;
int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits; int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits;
int data_offset_in_page; int data_offset_in_page;
int block_len_in_page; int block_len_in_page;
int uninit; int uninit;
/* orig and donor should be different file */ if (orig_inode->i_sb != donor_inode->i_sb) {
if (orig_inode->i_ino == donor_inode->i_ino) { ext4_debug("ext4 move extent: The argument files "
"should be in same FS [ino:orig %lu, donor %lu]\n",
orig_inode->i_ino, donor_inode->i_ino);
return -EINVAL;
}
/* orig and donor should be different inodes */
if (orig_inode == donor_inode) {
ext4_debug("ext4 move extent: The argument files should not " ext4_debug("ext4 move extent: The argument files should not "
"be same file [ino:orig %lu, donor %lu]\n", "be same inode [ino:orig %lu, donor %lu]\n",
orig_inode->i_ino, donor_inode->i_ino); orig_inode->i_ino, donor_inode->i_ino);
return -EINVAL; return -EINVAL;
} }
...@@ -1210,16 +1144,14 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1210,16 +1144,14 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
} }
/* Protect orig and donor inodes against a truncate */ /* Protect orig and donor inodes against a truncate */
ret1 = mext_inode_double_lock(orig_inode, donor_inode); mext_inode_double_lock(orig_inode, donor_inode);
if (ret1 < 0)
return ret1;
/* Protect extent tree against block allocations via delalloc */ /* Protect extent tree against block allocations via delalloc */
double_down_write_data_sem(orig_inode, donor_inode); double_down_write_data_sem(orig_inode, donor_inode);
/* Check the filesystem environment whether move_extent can be done */ /* Check the filesystem environment whether move_extent can be done */
ret1 = mext_check_arguments(orig_inode, donor_inode, orig_start, ret = mext_check_arguments(orig_inode, donor_inode, orig_start,
donor_start, &len); donor_start, &len);
if (ret1) if (ret)
goto out; goto out;
file_end = (i_size_read(orig_inode) - 1) >> orig_inode->i_blkbits; file_end = (i_size_read(orig_inode) - 1) >> orig_inode->i_blkbits;
...@@ -1227,13 +1159,13 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1227,13 +1159,13 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
if (file_end < block_end) if (file_end < block_end)
len -= block_end - file_end; len -= block_end - file_end;
ret1 = get_ext_path(orig_inode, block_start, &orig_path); ret = get_ext_path(orig_inode, block_start, &orig_path);
if (ret1) if (ret)
goto out; goto out;
/* Get path structure to check the hole */ /* Get path structure to check the hole */
ret1 = get_ext_path(orig_inode, block_start, &holecheck_path); ret = get_ext_path(orig_inode, block_start, &holecheck_path);
if (ret1) if (ret)
goto out; goto out;
depth = ext_depth(orig_inode); depth = ext_depth(orig_inode);
...@@ -1252,13 +1184,13 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1252,13 +1184,13 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
last_extent = mext_next_extent(orig_inode, last_extent = mext_next_extent(orig_inode,
holecheck_path, &ext_cur); holecheck_path, &ext_cur);
if (last_extent < 0) { if (last_extent < 0) {
ret1 = last_extent; ret = last_extent;
goto out; goto out;
} }
last_extent = mext_next_extent(orig_inode, orig_path, last_extent = mext_next_extent(orig_inode, orig_path,
&ext_dummy); &ext_dummy);
if (last_extent < 0) { if (last_extent < 0) {
ret1 = last_extent; ret = last_extent;
goto out; goto out;
} }
seq_start = le32_to_cpu(ext_cur->ee_block); seq_start = le32_to_cpu(ext_cur->ee_block);
...@@ -1272,7 +1204,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1272,7 +1204,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
if (le32_to_cpu(ext_cur->ee_block) > block_end) { if (le32_to_cpu(ext_cur->ee_block) > block_end) {
ext4_debug("ext4 move extent: The specified range of file " ext4_debug("ext4 move extent: The specified range of file "
"may be the hole\n"); "may be the hole\n");
ret1 = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
...@@ -1292,7 +1224,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1292,7 +1224,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
last_extent = mext_next_extent(orig_inode, holecheck_path, last_extent = mext_next_extent(orig_inode, holecheck_path,
&ext_cur); &ext_cur);
if (last_extent < 0) { if (last_extent < 0) {
ret1 = last_extent; ret = last_extent;
break; break;
} }
add_blocks = ext4_ext_get_actual_len(ext_cur); add_blocks = ext4_ext_get_actual_len(ext_cur);
...@@ -1349,18 +1281,18 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1349,18 +1281,18 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
orig_page_offset, orig_page_offset,
data_offset_in_page, data_offset_in_page,
block_len_in_page, uninit, block_len_in_page, uninit,
&ret1); &ret);
/* Count how many blocks we have exchanged */ /* Count how many blocks we have exchanged */
*moved_len += block_len_in_page; *moved_len += block_len_in_page;
if (ret1 < 0) if (ret < 0)
break; break;
if (*moved_len > len) { if (*moved_len > len) {
EXT4_ERROR_INODE(orig_inode, EXT4_ERROR_INODE(orig_inode,
"We replaced blocks too much! " "We replaced blocks too much! "
"sum of replaced: %llu requested: %llu", "sum of replaced: %llu requested: %llu",
*moved_len, len); *moved_len, len);
ret1 = -EIO; ret = -EIO;
break; break;
} }
...@@ -1374,22 +1306,22 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1374,22 +1306,22 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
} }
double_down_write_data_sem(orig_inode, donor_inode); double_down_write_data_sem(orig_inode, donor_inode);
if (ret1 < 0) if (ret < 0)
break; break;
/* Decrease buffer counter */ /* Decrease buffer counter */
if (holecheck_path) if (holecheck_path)
ext4_ext_drop_refs(holecheck_path); ext4_ext_drop_refs(holecheck_path);
ret1 = get_ext_path(orig_inode, seq_start, &holecheck_path); ret = get_ext_path(orig_inode, seq_start, &holecheck_path);
if (ret1) if (ret)
break; break;
depth = holecheck_path->p_depth; depth = holecheck_path->p_depth;
/* Decrease buffer counter */ /* Decrease buffer counter */
if (orig_path) if (orig_path)
ext4_ext_drop_refs(orig_path); ext4_ext_drop_refs(orig_path);
ret1 = get_ext_path(orig_inode, seq_start, &orig_path); ret = get_ext_path(orig_inode, seq_start, &orig_path);
if (ret1) if (ret)
break; break;
ext_cur = holecheck_path[depth].p_ext; ext_cur = holecheck_path[depth].p_ext;
...@@ -1412,12 +1344,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, ...@@ -1412,12 +1344,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
kfree(holecheck_path); kfree(holecheck_path);
} }
double_up_write_data_sem(orig_inode, donor_inode); double_up_write_data_sem(orig_inode, donor_inode);
ret2 = mext_inode_double_unlock(orig_inode, donor_inode); mext_inode_double_unlock(orig_inode, donor_inode);
if (ret1) return ret;
return ret1;
else if (ret2)
return ret2;
return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment