2017-12-18 11:00:59 +08:00
|
|
|
// SPDX-License-Identifier: LGPL-2.1
|
2008-01-29 12:58:26 +08:00
|
|
|
/*
|
|
|
|
* Copyright IBM Corporation, 2007
|
|
|
|
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2008-04-30 06:13:32 +08:00
|
|
|
#include "ext4_jbd2.h"
|
2012-11-29 02:03:30 +08:00
|
|
|
#include "ext4_extents.h"
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The contiguous blocks details which can be
|
|
|
|
* represented by a single extent
|
|
|
|
*/
|
2011-10-29 21:03:00 +08:00
|
|
|
struct migrate_struct {
|
|
|
|
ext4_lblk_t first_block, last_block, curr_block;
|
2008-01-29 12:58:26 +08:00
|
|
|
ext4_fsblk_t first_pblock, last_pblock;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int finish_range(handle_t *handle, struct inode *inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
struct migrate_struct *lb)
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
{
|
|
|
|
int retval = 0, needed;
|
|
|
|
struct ext4_extent newext;
|
|
|
|
struct ext4_ext_path *path;
|
|
|
|
if (lb->first_pblock == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Add the extent to temp inode*/
|
|
|
|
newext.ee_block = cpu_to_le32(lb->first_block);
|
|
|
|
newext.ee_len = cpu_to_le16(lb->last_block - lb->first_block + 1);
|
|
|
|
ext4_ext_store_pblock(&newext, lb->first_pblock);
|
2021-03-27 18:30:05 +08:00
|
|
|
/* Locking only for convenience since we are operating on temp inode */
|
2014-07-28 10:28:15 +08:00
|
|
|
down_write(&EXT4_I(inode)->i_data_sem);
|
2014-09-02 02:43:09 +08:00
|
|
|
path = ext4_find_extent(inode, lb->first_block, NULL, 0);
|
2008-01-29 12:58:26 +08:00
|
|
|
if (IS_ERR(path)) {
|
|
|
|
retval = PTR_ERR(path);
|
2008-02-26 05:54:37 +08:00
|
|
|
path = NULL;
|
2008-01-29 12:58:26 +08:00
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the credit needed to inserting this extent
|
2021-03-27 18:30:05 +08:00
|
|
|
* Since we are doing this in loop we may accumulate extra
|
|
|
|
* credit. But below we try to not accumulate too much
|
2008-01-29 12:58:26 +08:00
|
|
|
* of them by restarting the journal.
|
|
|
|
*/
|
2008-08-20 10:16:05 +08:00
|
|
|
needed = ext4_ext_calc_credits_for_single_extent(inode,
|
|
|
|
lb->last_block - lb->first_block + 1, path);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
2019-11-06 00:44:29 +08:00
|
|
|
retval = ext4_datasem_ensure_credits(handle, inode, needed, needed, 0);
|
2019-11-06 00:44:16 +08:00
|
|
|
if (retval < 0)
|
|
|
|
goto err_out;
|
2014-09-02 02:37:09 +08:00
|
|
|
retval = ext4_ext_insert_extent(handle, inode, &path, &newext, 0);
|
2008-01-29 12:58:26 +08:00
|
|
|
err_out:
|
2014-07-28 10:28:15 +08:00
|
|
|
up_write((&EXT4_I(inode)->i_data_sem));
|
2022-09-24 10:12:11 +08:00
|
|
|
ext4_free_ext_path(path);
|
2008-01-29 12:58:26 +08:00
|
|
|
lb->first_pblock = 0;
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int update_extent_range(handle_t *handle, struct inode *inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
ext4_fsblk_t pblock, struct migrate_struct *lb)
|
2008-01-29 12:58:26 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
/*
|
|
|
|
* See if we can add on to the existing range (if it exists)
|
|
|
|
*/
|
|
|
|
if (lb->first_pblock &&
|
|
|
|
(lb->last_pblock+1 == pblock) &&
|
2011-10-29 21:03:00 +08:00
|
|
|
(lb->last_block+1 == lb->curr_block)) {
|
2008-01-29 12:58:26 +08:00
|
|
|
lb->last_pblock = pblock;
|
2011-10-29 21:03:00 +08:00
|
|
|
lb->last_block = lb->curr_block;
|
|
|
|
lb->curr_block++;
|
2008-01-29 12:58:26 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Start a new range.
|
|
|
|
*/
|
|
|
|
retval = finish_range(handle, inode, lb);
|
|
|
|
lb->first_pblock = lb->last_pblock = pblock;
|
2011-10-29 21:03:00 +08:00
|
|
|
lb->first_block = lb->last_block = lb->curr_block;
|
|
|
|
lb->curr_block++;
|
2008-01-29 12:58:26 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int update_ind_extent_range(handle_t *handle, struct inode *inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
ext4_fsblk_t pblock,
|
|
|
|
struct migrate_struct *lb)
|
2008-01-29 12:58:26 +08:00
|
|
|
{
|
|
|
|
struct buffer_head *bh;
|
|
|
|
__le32 *i_data;
|
|
|
|
int i, retval = 0;
|
|
|
|
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;
|
|
|
|
|
2018-11-26 06:20:31 +08:00
|
|
|
bh = ext4_sb_bread(inode->i_sb, pblock, 0);
|
|
|
|
if (IS_ERR(bh))
|
|
|
|
return PTR_ERR(bh);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
i_data = (__le32 *)bh->b_data;
|
2011-10-29 21:03:00 +08:00
|
|
|
for (i = 0; i < max_entries; i++) {
|
2008-01-29 12:58:26 +08:00
|
|
|
if (i_data[i]) {
|
|
|
|
retval = update_extent_range(handle, inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
le32_to_cpu(i_data[i]), lb);
|
2008-01-29 12:58:26 +08:00
|
|
|
if (retval)
|
|
|
|
break;
|
2011-10-29 21:03:00 +08:00
|
|
|
} else {
|
|
|
|
lb->curr_block++;
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
put_bh(bh);
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int update_dind_extent_range(handle_t *handle, struct inode *inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
ext4_fsblk_t pblock,
|
|
|
|
struct migrate_struct *lb)
|
2008-01-29 12:58:26 +08:00
|
|
|
{
|
|
|
|
struct buffer_head *bh;
|
|
|
|
__le32 *i_data;
|
|
|
|
int i, retval = 0;
|
|
|
|
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;
|
|
|
|
|
2018-11-26 06:20:31 +08:00
|
|
|
bh = ext4_sb_bread(inode->i_sb, pblock, 0);
|
|
|
|
if (IS_ERR(bh))
|
|
|
|
return PTR_ERR(bh);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
i_data = (__le32 *)bh->b_data;
|
|
|
|
for (i = 0; i < max_entries; i++) {
|
|
|
|
if (i_data[i]) {
|
|
|
|
retval = update_ind_extent_range(handle, inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
le32_to_cpu(i_data[i]), lb);
|
2008-01-29 12:58:26 +08:00
|
|
|
if (retval)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
/* Only update the file block number */
|
2011-10-29 21:03:00 +08:00
|
|
|
lb->curr_block += max_entries;
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
put_bh(bh);
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int update_tind_extent_range(handle_t *handle, struct inode *inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
ext4_fsblk_t pblock,
|
|
|
|
struct migrate_struct *lb)
|
2008-01-29 12:58:26 +08:00
|
|
|
{
|
|
|
|
struct buffer_head *bh;
|
|
|
|
__le32 *i_data;
|
|
|
|
int i, retval = 0;
|
|
|
|
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;
|
|
|
|
|
2018-11-26 06:20:31 +08:00
|
|
|
bh = ext4_sb_bread(inode->i_sb, pblock, 0);
|
|
|
|
if (IS_ERR(bh))
|
|
|
|
return PTR_ERR(bh);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
i_data = (__le32 *)bh->b_data;
|
|
|
|
for (i = 0; i < max_entries; i++) {
|
|
|
|
if (i_data[i]) {
|
|
|
|
retval = update_dind_extent_range(handle, inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
le32_to_cpu(i_data[i]), lb);
|
2008-01-29 12:58:26 +08:00
|
|
|
if (retval)
|
|
|
|
break;
|
2011-10-29 21:03:00 +08:00
|
|
|
} else {
|
2008-01-29 12:58:26 +08:00
|
|
|
/* Only update the file block number */
|
2011-10-29 21:03:00 +08:00
|
|
|
lb->curr_block += max_entries * max_entries;
|
|
|
|
}
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
|
|
|
put_bh(bh);
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int free_dind_blocks(handle_t *handle,
|
|
|
|
struct inode *inode, __le32 i_data)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
__le32 *tmp_idata;
|
|
|
|
struct buffer_head *bh;
|
2019-11-06 00:44:29 +08:00
|
|
|
struct super_block *sb = inode->i_sb;
|
2008-01-29 12:58:26 +08:00
|
|
|
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;
|
2019-11-06 00:44:16 +08:00
|
|
|
int err;
|
2008-01-29 12:58:26 +08:00
|
|
|
|
2019-11-06 00:44:29 +08:00
|
|
|
bh = ext4_sb_bread(sb, le32_to_cpu(i_data), 0);
|
2018-11-26 06:20:31 +08:00
|
|
|
if (IS_ERR(bh))
|
|
|
|
return PTR_ERR(bh);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
tmp_idata = (__le32 *)bh->b_data;
|
|
|
|
for (i = 0; i < max_entries; i++) {
|
2008-02-10 14:20:05 +08:00
|
|
|
if (tmp_idata[i]) {
|
2019-11-06 00:44:16 +08:00
|
|
|
err = ext4_journal_ensure_credits(handle,
|
2019-11-06 00:44:29 +08:00
|
|
|
EXT4_RESERVE_TRANS_BLOCKS,
|
|
|
|
ext4_free_metadata_revoke_credits(sb, 1));
|
2019-11-06 00:44:16 +08:00
|
|
|
if (err < 0) {
|
|
|
|
put_bh(bh);
|
|
|
|
return err;
|
|
|
|
}
|
2011-02-22 10:01:42 +08:00
|
|
|
ext4_free_blocks(handle, inode, NULL,
|
2009-11-23 20:17:05 +08:00
|
|
|
le32_to_cpu(tmp_idata[i]), 1,
|
|
|
|
EXT4_FREE_BLOCKS_METADATA |
|
|
|
|
EXT4_FREE_BLOCKS_FORGET);
|
2008-02-10 14:20:05 +08:00
|
|
|
}
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
|
|
|
put_bh(bh);
|
2019-11-06 00:44:29 +08:00
|
|
|
err = ext4_journal_ensure_credits(handle, EXT4_RESERVE_TRANS_BLOCKS,
|
|
|
|
ext4_free_metadata_revoke_credits(sb, 1));
|
2019-11-06 00:44:16 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2011-02-22 10:01:42 +08:00
|
|
|
ext4_free_blocks(handle, inode, NULL, le32_to_cpu(i_data), 1,
|
2009-11-23 20:17:05 +08:00
|
|
|
EXT4_FREE_BLOCKS_METADATA |
|
|
|
|
EXT4_FREE_BLOCKS_FORGET);
|
2008-01-29 12:58:26 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int free_tind_blocks(handle_t *handle,
|
|
|
|
struct inode *inode, __le32 i_data)
|
|
|
|
{
|
|
|
|
int i, retval = 0;
|
|
|
|
__le32 *tmp_idata;
|
|
|
|
struct buffer_head *bh;
|
|
|
|
unsigned long max_entries = inode->i_sb->s_blocksize >> 2;
|
|
|
|
|
2018-11-26 06:20:31 +08:00
|
|
|
bh = ext4_sb_bread(inode->i_sb, le32_to_cpu(i_data), 0);
|
|
|
|
if (IS_ERR(bh))
|
|
|
|
return PTR_ERR(bh);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
tmp_idata = (__le32 *)bh->b_data;
|
|
|
|
for (i = 0; i < max_entries; i++) {
|
|
|
|
if (tmp_idata[i]) {
|
|
|
|
retval = free_dind_blocks(handle,
|
|
|
|
inode, tmp_idata[i]);
|
|
|
|
if (retval) {
|
|
|
|
put_bh(bh);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
put_bh(bh);
|
2019-11-06 00:44:29 +08:00
|
|
|
retval = ext4_journal_ensure_credits(handle, EXT4_RESERVE_TRANS_BLOCKS,
|
|
|
|
ext4_free_metadata_revoke_credits(inode->i_sb, 1));
|
2019-11-06 00:44:16 +08:00
|
|
|
if (retval < 0)
|
|
|
|
return retval;
|
2011-02-22 10:01:42 +08:00
|
|
|
ext4_free_blocks(handle, inode, NULL, le32_to_cpu(i_data), 1,
|
2009-11-23 20:17:05 +08:00
|
|
|
EXT4_FREE_BLOCKS_METADATA |
|
|
|
|
EXT4_FREE_BLOCKS_FORGET);
|
2008-01-29 12:58:26 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-10 14:20:05 +08:00
|
|
|
static int free_ind_block(handle_t *handle, struct inode *inode, __le32 *i_data)
|
2008-01-29 12:58:26 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
|
2008-02-10 14:20:05 +08:00
|
|
|
/* ei->i_data[EXT4_IND_BLOCK] */
|
|
|
|
if (i_data[0]) {
|
2019-11-06 00:44:16 +08:00
|
|
|
retval = ext4_journal_ensure_credits(handle,
|
2019-11-06 00:44:29 +08:00
|
|
|
EXT4_RESERVE_TRANS_BLOCKS,
|
|
|
|
ext4_free_metadata_revoke_credits(inode->i_sb, 1));
|
2019-11-06 00:44:16 +08:00
|
|
|
if (retval < 0)
|
|
|
|
return retval;
|
2011-02-22 10:01:42 +08:00
|
|
|
ext4_free_blocks(handle, inode, NULL,
|
2009-11-23 20:17:05 +08:00
|
|
|
le32_to_cpu(i_data[0]), 1,
|
|
|
|
EXT4_FREE_BLOCKS_METADATA |
|
|
|
|
EXT4_FREE_BLOCKS_FORGET);
|
2008-02-10 14:20:05 +08:00
|
|
|
}
|
2008-01-29 12:58:26 +08:00
|
|
|
|
2008-02-10 14:20:05 +08:00
|
|
|
/* ei->i_data[EXT4_DIND_BLOCK] */
|
|
|
|
if (i_data[1]) {
|
|
|
|
retval = free_dind_blocks(handle, inode, i_data[1]);
|
2008-01-29 12:58:26 +08:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-02-10 14:20:05 +08:00
|
|
|
/* ei->i_data[EXT4_TIND_BLOCK] */
|
|
|
|
if (i_data[2]) {
|
|
|
|
retval = free_tind_blocks(handle, inode, i_data[2]);
|
2008-01-29 12:58:26 +08:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode,
|
2008-04-29 20:11:12 +08:00
|
|
|
struct inode *tmp_inode)
|
2008-01-29 12:58:26 +08:00
|
|
|
{
|
2020-04-27 09:34:37 +08:00
|
|
|
int retval, retval2 = 0;
|
2008-02-10 14:20:05 +08:00
|
|
|
__le32 i_data[3];
|
2008-01-29 12:58:26 +08:00
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
|
|
struct ext4_inode_info *tmp_ei = EXT4_I(tmp_inode);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* One credit accounted for writing the
|
|
|
|
* i_data field of the original inode
|
|
|
|
*/
|
2019-11-06 00:44:29 +08:00
|
|
|
retval = ext4_journal_ensure_credits(handle, 1, 0);
|
2019-11-06 00:44:16 +08:00
|
|
|
if (retval < 0)
|
|
|
|
goto err_out;
|
2008-01-29 12:58:26 +08:00
|
|
|
|
2008-02-10 14:20:05 +08:00
|
|
|
i_data[0] = ei->i_data[EXT4_IND_BLOCK];
|
|
|
|
i_data[1] = ei->i_data[EXT4_DIND_BLOCK];
|
|
|
|
i_data[2] = ei->i_data[EXT4_TIND_BLOCK];
|
|
|
|
|
|
|
|
down_write(&EXT4_I(inode)->i_data_sem);
|
2008-04-29 20:11:12 +08:00
|
|
|
/*
|
2009-09-17 20:32:22 +08:00
|
|
|
* if EXT4_STATE_EXT_MIGRATE is cleared a block allocation
|
2008-04-29 20:11:12 +08:00
|
|
|
* happened after we started the migrate. We need to
|
|
|
|
* fail the migrate
|
|
|
|
*/
|
2010-01-25 03:34:07 +08:00
|
|
|
if (!ext4_test_inode_state(inode, EXT4_STATE_EXT_MIGRATE)) {
|
2008-04-29 20:11:12 +08:00
|
|
|
retval = -EAGAIN;
|
|
|
|
up_write(&EXT4_I(inode)->i_data_sem);
|
|
|
|
goto err_out;
|
|
|
|
} else
|
2010-01-25 03:34:07 +08:00
|
|
|
ext4_clear_inode_state(inode, EXT4_STATE_EXT_MIGRATE);
|
2008-01-29 12:58:26 +08:00
|
|
|
/*
|
|
|
|
* We have the extent map build with the tmp inode.
|
|
|
|
* Now copy the i_data across
|
|
|
|
*/
|
2011-05-03 21:34:42 +08:00
|
|
|
ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS);
|
2008-01-29 12:58:26 +08:00
|
|
|
memcpy(ei->i_data, tmp_ei->i_data, sizeof(ei->i_data));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update i_blocks with the new blocks that got
|
|
|
|
* allocated while adding extents for extent index
|
|
|
|
* blocks.
|
|
|
|
*
|
|
|
|
* While converting to extents we need not
|
2016-03-10 12:49:05 +08:00
|
|
|
* update the original inode i_blocks for extent blocks
|
2008-01-29 12:58:26 +08:00
|
|
|
* via quota APIs. The quota update happened via tmp_inode already.
|
|
|
|
*/
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
inode->i_blocks += tmp_inode->i_blocks;
|
|
|
|
spin_unlock(&inode->i_lock);
|
2008-02-10 14:20:05 +08:00
|
|
|
up_write(&EXT4_I(inode)->i_data_sem);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
2008-02-10 14:20:05 +08:00
|
|
|
/*
|
|
|
|
* We mark the inode dirty after, because we decrement the
|
|
|
|
* i_blocks when freeing the indirect meta-data blocks
|
|
|
|
*/
|
|
|
|
retval = free_ind_block(handle, inode, i_data);
|
2020-04-27 09:34:37 +08:00
|
|
|
retval2 = ext4_mark_inode_dirty(handle, inode);
|
|
|
|
if (unlikely(retval2 && !retval))
|
|
|
|
retval = retval2;
|
2008-02-10 14:20:05 +08:00
|
|
|
|
2008-01-29 12:58:26 +08:00
|
|
|
err_out:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int free_ext_idx(handle_t *handle, struct inode *inode,
|
|
|
|
struct ext4_extent_idx *ix)
|
|
|
|
{
|
|
|
|
int i, retval = 0;
|
|
|
|
ext4_fsblk_t block;
|
|
|
|
struct buffer_head *bh;
|
|
|
|
struct ext4_extent_header *eh;
|
|
|
|
|
2010-10-28 09:30:14 +08:00
|
|
|
block = ext4_idx_pblock(ix);
|
2018-11-26 06:20:31 +08:00
|
|
|
bh = ext4_sb_bread(inode->i_sb, block, 0);
|
|
|
|
if (IS_ERR(bh))
|
|
|
|
return PTR_ERR(bh);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
eh = (struct ext4_extent_header *)bh->b_data;
|
|
|
|
if (eh->eh_depth != 0) {
|
|
|
|
ix = EXT_FIRST_INDEX(eh);
|
|
|
|
for (i = 0; i < le16_to_cpu(eh->eh_entries); i++, ix++) {
|
|
|
|
retval = free_ext_idx(handle, inode, ix);
|
2019-11-06 00:44:16 +08:00
|
|
|
if (retval) {
|
|
|
|
put_bh(bh);
|
|
|
|
return retval;
|
|
|
|
}
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
put_bh(bh);
|
2019-11-06 00:44:29 +08:00
|
|
|
retval = ext4_journal_ensure_credits(handle, EXT4_RESERVE_TRANS_BLOCKS,
|
|
|
|
ext4_free_metadata_revoke_credits(inode->i_sb, 1));
|
2019-11-06 00:44:16 +08:00
|
|
|
if (retval < 0)
|
|
|
|
return retval;
|
2011-02-22 10:01:42 +08:00
|
|
|
ext4_free_blocks(handle, inode, NULL, block, 1,
|
2009-11-23 20:17:05 +08:00
|
|
|
EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
|
2019-11-06 00:44:16 +08:00
|
|
|
return 0;
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free the extent meta data blocks only
|
|
|
|
*/
|
|
|
|
static int free_ext_block(handle_t *handle, struct inode *inode)
|
|
|
|
{
|
|
|
|
int i, retval = 0;
|
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
|
|
struct ext4_extent_header *eh = (struct ext4_extent_header *)ei->i_data;
|
|
|
|
struct ext4_extent_idx *ix;
|
|
|
|
if (eh->eh_depth == 0)
|
|
|
|
/*
|
|
|
|
* No extra blocks allocated for extent meta data
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
ix = EXT_FIRST_INDEX(eh);
|
|
|
|
for (i = 0; i < le16_to_cpu(eh->eh_entries); i++, ix++) {
|
|
|
|
retval = free_ext_idx(handle, inode, ix);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-09-14 00:52:26 +08:00
|
|
|
int ext4_ext_migrate(struct inode *inode)
|
2008-01-29 12:58:26 +08:00
|
|
|
{
|
|
|
|
handle_t *handle;
|
|
|
|
int retval = 0, i;
|
|
|
|
__le32 *i_data;
|
|
|
|
struct ext4_inode_info *ei;
|
|
|
|
struct inode *tmp_inode = NULL;
|
2011-10-29 21:03:00 +08:00
|
|
|
struct migrate_struct lb;
|
2008-01-29 12:58:26 +08:00
|
|
|
unsigned long max_entries;
|
2022-06-17 14:25:15 +08:00
|
|
|
__u32 goal, tmp_csum_seed;
|
2011-10-29 21:05:00 +08:00
|
|
|
uid_t owner[2];
|
2023-05-04 20:47:23 +08:00
|
|
|
int alloc_ctx;
|
2008-01-29 12:58:26 +08:00
|
|
|
|
2009-01-07 03:53:16 +08:00
|
|
|
/*
|
|
|
|
* If the filesystem does not support extents, or the inode
|
|
|
|
* already is extent-based, error out.
|
|
|
|
*/
|
2015-10-18 04:18:43 +08:00
|
|
|
if (!ext4_has_feature_extents(inode->i_sb) ||
|
ext4: fix warning in 'ext4_da_release_space'
Syzkaller report issue as follows:
EXT4-fs (loop0): Free/Dirty block details
EXT4-fs (loop0): free_blocks=0
EXT4-fs (loop0): dirty_blocks=0
EXT4-fs (loop0): Block reservation details
EXT4-fs (loop0): i_reserved_data_blocks=0
EXT4-fs warning (device loop0): ext4_da_release_space:1527: ext4_da_release_space: ino 18, to_free 1 with only 0 reserved data blocks
------------[ cut here ]------------
WARNING: CPU: 0 PID: 92 at fs/ext4/inode.c:1528 ext4_da_release_space+0x25e/0x370 fs/ext4/inode.c:1524
Modules linked in:
CPU: 0 PID: 92 Comm: kworker/u4:4 Not tainted 6.0.0-syzkaller-09423-g493ffd6605b2 #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 09/22/2022
Workqueue: writeback wb_workfn (flush-7:0)
RIP: 0010:ext4_da_release_space+0x25e/0x370 fs/ext4/inode.c:1528
RSP: 0018:ffffc900015f6c90 EFLAGS: 00010296
RAX: 42215896cd52ea00 RBX: 0000000000000000 RCX: 42215896cd52ea00
RDX: 0000000000000000 RSI: 0000000080000001 RDI: 0000000000000000
RBP: 1ffff1100e907d96 R08: ffffffff816aa79d R09: fffff520002bece5
R10: fffff520002bece5 R11: 1ffff920002bece4 R12: ffff888021fd2000
R13: ffff88807483ecb0 R14: 0000000000000001 R15: ffff88807483e740
FS: 0000000000000000(0000) GS:ffff8880b9a00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00005555569ba628 CR3: 000000000c88e000 CR4: 00000000003506f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<TASK>
ext4_es_remove_extent+0x1ab/0x260 fs/ext4/extents_status.c:1461
mpage_release_unused_pages+0x24d/0xef0 fs/ext4/inode.c:1589
ext4_writepages+0x12eb/0x3be0 fs/ext4/inode.c:2852
do_writepages+0x3c3/0x680 mm/page-writeback.c:2469
__writeback_single_inode+0xd1/0x670 fs/fs-writeback.c:1587
writeback_sb_inodes+0xb3b/0x18f0 fs/fs-writeback.c:1870
wb_writeback+0x41f/0x7b0 fs/fs-writeback.c:2044
wb_do_writeback fs/fs-writeback.c:2187 [inline]
wb_workfn+0x3cb/0xef0 fs/fs-writeback.c:2227
process_one_work+0x877/0xdb0 kernel/workqueue.c:2289
worker_thread+0xb14/0x1330 kernel/workqueue.c:2436
kthread+0x266/0x300 kernel/kthread.c:376
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:306
</TASK>
Above issue may happens as follows:
ext4_da_write_begin
ext4_create_inline_data
ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
ext4_set_inode_flag(inode, EXT4_INODE_INLINE_DATA);
__ext4_ioctl
ext4_ext_migrate -> will lead to eh->eh_entries not zero, and set extent flag
ext4_da_write_begin
ext4_da_convert_inline_data_to_extent
ext4_da_write_inline_data_begin
ext4_da_map_blocks
ext4_insert_delayed_block
if (!ext4_es_scan_clu(inode, &ext4_es_is_delonly, lblk))
if (!ext4_es_scan_clu(inode, &ext4_es_is_mapped, lblk))
ext4_clu_mapped(inode, EXT4_B2C(sbi, lblk)); -> will return 1
allocated = true;
ext4_es_insert_delayed_block(inode, lblk, allocated);
ext4_writepages
mpage_map_and_submit_extent(handle, &mpd, &give_up_on_write); -> return -ENOSPC
mpage_release_unused_pages(&mpd, give_up_on_write); -> give_up_on_write == 1
ext4_es_remove_extent
ext4_da_release_space(inode, reserved);
if (unlikely(to_free > ei->i_reserved_data_blocks))
-> to_free == 1 but ei->i_reserved_data_blocks == 0
-> then trigger warning as above
To solve above issue, forbid inode do migrate which has inline data.
Cc: stable@kernel.org
Reported-by: syzbot+c740bb18df70ad00952e@syzkaller.appspotmail.com
Signed-off-by: Ye Bin <yebin10@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20221018022701.683489-1-yebin10@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2022-10-18 10:27:01 +08:00
|
|
|
ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS) ||
|
|
|
|
ext4_has_inline_data(inode))
|
2008-01-29 12:58:26 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-02-05 23:56:37 +08:00
|
|
|
if (S_ISLNK(inode->i_mode) && inode->i_blocks == 0)
|
|
|
|
/*
|
|
|
|
* don't migrate fast symlink
|
|
|
|
*/
|
|
|
|
return retval;
|
|
|
|
|
2023-05-04 20:47:23 +08:00
|
|
|
alloc_ctx = ext4_writepages_down_write(inode->i_sb);
|
2020-02-20 02:30:47 +08:00
|
|
|
|
2013-02-10 01:50:27 +08:00
|
|
|
/*
|
2022-01-06 12:59:56 +08:00
|
|
|
* Worst case we can touch the allocation bitmaps and a block
|
2022-06-05 17:15:03 +08:00
|
|
|
* group descriptor block. We do need to worry about
|
2022-01-06 12:59:56 +08:00
|
|
|
* credits for modifying the quota inode.
|
2013-02-10 01:50:27 +08:00
|
|
|
*/
|
2013-02-09 10:59:22 +08:00
|
|
|
handle = ext4_journal_start(inode, EXT4_HT_MIGRATE,
|
2022-01-06 12:59:56 +08:00
|
|
|
3 + EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb));
|
2013-02-10 01:50:27 +08:00
|
|
|
|
2008-01-29 12:58:26 +08:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
retval = PTR_ERR(handle);
|
2020-02-20 02:30:47 +08:00
|
|
|
goto out_unlock;
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
2009-06-13 23:45:35 +08:00
|
|
|
goal = (((inode->i_ino - 1) / EXT4_INODES_PER_GROUP(inode->i_sb)) *
|
|
|
|
EXT4_INODES_PER_GROUP(inode->i_sb)) + 1;
|
2012-02-08 07:41:49 +08:00
|
|
|
owner[0] = i_uid_read(inode);
|
|
|
|
owner[1] = i_gid_read(inode);
|
2015-03-18 06:25:59 +08:00
|
|
|
tmp_inode = ext4_new_inode(handle, d_inode(inode->i_sb->s_root),
|
2017-06-22 09:21:39 +08:00
|
|
|
S_IFREG, NULL, goal, owner, 0);
|
2008-01-29 12:58:26 +08:00
|
|
|
if (IS_ERR(tmp_inode)) {
|
2012-02-21 06:53:06 +08:00
|
|
|
retval = PTR_ERR(tmp_inode);
|
2008-01-29 12:58:26 +08:00
|
|
|
ext4_journal_stop(handle);
|
2020-02-20 02:30:47 +08:00
|
|
|
goto out_unlock;
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
2021-12-15 01:50:58 +08:00
|
|
|
/*
|
|
|
|
* Use the correct seed for checksum (i.e. the seed from 'inode'). This
|
|
|
|
* is so that the metadata blocks will have the correct checksum after
|
|
|
|
* the migration.
|
|
|
|
*/
|
|
|
|
ei = EXT4_I(inode);
|
2022-06-17 14:25:15 +08:00
|
|
|
tmp_csum_seed = EXT4_I(tmp_inode)->i_csum_seed;
|
2021-12-15 01:50:58 +08:00
|
|
|
EXT4_I(tmp_inode)->i_csum_seed = ei->i_csum_seed;
|
2008-01-29 12:58:26 +08:00
|
|
|
i_size_write(tmp_inode, i_size_read(inode));
|
|
|
|
/*
|
2010-03-02 12:14:36 +08:00
|
|
|
* Set the i_nlink to zero so it will be deleted later
|
|
|
|
* when we drop inode reference.
|
2008-01-29 12:58:26 +08:00
|
|
|
*/
|
2011-10-28 20:13:28 +08:00
|
|
|
clear_nlink(tmp_inode);
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
ext4_ext_tree_init(handle, tmp_inode);
|
|
|
|
ext4_journal_stop(handle);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* start with one credit accounted for
|
|
|
|
* superblock modification.
|
|
|
|
*
|
2011-03-31 09:57:33 +08:00
|
|
|
* For the tmp_inode we already have committed the
|
2013-08-29 02:40:12 +08:00
|
|
|
* transaction that created the inode. Later as and
|
2008-01-29 12:58:26 +08:00
|
|
|
* when we add extents we extent the journal
|
|
|
|
*/
|
2008-04-29 20:11:12 +08:00
|
|
|
/*
|
2022-01-21 15:06:11 +08:00
|
|
|
* Even though we take i_rwsem we can still cause block
|
2009-09-17 20:32:22 +08:00
|
|
|
* allocation via mmap write to holes. If we have allocated
|
|
|
|
* new blocks we fail migrate. New block allocation will
|
|
|
|
* clear EXT4_STATE_EXT_MIGRATE flag. The flag is updated
|
|
|
|
* with i_data_sem held to prevent racing with block
|
|
|
|
* allocation.
|
2008-04-29 20:11:12 +08:00
|
|
|
*/
|
2014-05-13 00:55:07 +08:00
|
|
|
down_read(&EXT4_I(inode)->i_data_sem);
|
2010-01-25 03:34:07 +08:00
|
|
|
ext4_set_inode_state(inode, EXT4_STATE_EXT_MIGRATE);
|
2008-04-29 20:11:12 +08:00
|
|
|
up_read((&EXT4_I(inode)->i_data_sem));
|
|
|
|
|
2013-02-09 10:59:22 +08:00
|
|
|
handle = ext4_journal_start(inode, EXT4_HT_MIGRATE, 1);
|
2010-03-02 12:14:36 +08:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
retval = PTR_ERR(handle);
|
2020-02-20 02:30:47 +08:00
|
|
|
goto out_tmp_inode;
|
2010-03-02 12:14:36 +08:00
|
|
|
}
|
2008-02-10 14:20:05 +08:00
|
|
|
|
|
|
|
i_data = ei->i_data;
|
|
|
|
memset(&lb, 0, sizeof(lb));
|
|
|
|
|
|
|
|
/* 32 bit block address 4 bytes */
|
|
|
|
max_entries = inode->i_sb->s_blocksize >> 2;
|
2011-10-29 21:03:00 +08:00
|
|
|
for (i = 0; i < EXT4_NDIR_BLOCKS; i++) {
|
2008-01-29 12:58:26 +08:00
|
|
|
if (i_data[i]) {
|
|
|
|
retval = update_extent_range(handle, tmp_inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
le32_to_cpu(i_data[i]), &lb);
|
2008-01-29 12:58:26 +08:00
|
|
|
if (retval)
|
|
|
|
goto err_out;
|
2011-10-29 21:03:00 +08:00
|
|
|
} else
|
|
|
|
lb.curr_block++;
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
|
|
|
if (i_data[EXT4_IND_BLOCK]) {
|
|
|
|
retval = update_ind_extent_range(handle, tmp_inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
le32_to_cpu(i_data[EXT4_IND_BLOCK]), &lb);
|
2018-12-04 13:16:44 +08:00
|
|
|
if (retval)
|
|
|
|
goto err_out;
|
2008-01-29 12:58:26 +08:00
|
|
|
} else
|
2011-10-29 21:03:00 +08:00
|
|
|
lb.curr_block += max_entries;
|
2008-01-29 12:58:26 +08:00
|
|
|
if (i_data[EXT4_DIND_BLOCK]) {
|
|
|
|
retval = update_dind_extent_range(handle, tmp_inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
le32_to_cpu(i_data[EXT4_DIND_BLOCK]), &lb);
|
2018-12-04 13:16:44 +08:00
|
|
|
if (retval)
|
|
|
|
goto err_out;
|
2008-01-29 12:58:26 +08:00
|
|
|
} else
|
2011-10-29 21:03:00 +08:00
|
|
|
lb.curr_block += max_entries * max_entries;
|
2008-01-29 12:58:26 +08:00
|
|
|
if (i_data[EXT4_TIND_BLOCK]) {
|
|
|
|
retval = update_tind_extent_range(handle, tmp_inode,
|
2011-10-29 21:03:00 +08:00
|
|
|
le32_to_cpu(i_data[EXT4_TIND_BLOCK]), &lb);
|
2018-12-04 13:16:44 +08:00
|
|
|
if (retval)
|
|
|
|
goto err_out;
|
2008-01-29 12:58:26 +08:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Build the last extent
|
|
|
|
*/
|
|
|
|
retval = finish_range(handle, tmp_inode, &lb);
|
|
|
|
err_out:
|
|
|
|
if (retval)
|
|
|
|
/*
|
|
|
|
* Failure case delete the extent information with the
|
|
|
|
* tmp_inode
|
|
|
|
*/
|
|
|
|
free_ext_block(handle, tmp_inode);
|
2008-04-29 20:11:12 +08:00
|
|
|
else {
|
|
|
|
retval = ext4_ext_swap_inode_data(handle, inode, tmp_inode);
|
|
|
|
if (retval)
|
|
|
|
/*
|
|
|
|
* if we fail to swap inode data free the extent
|
|
|
|
* details of the tmp inode
|
|
|
|
*/
|
|
|
|
free_ext_block(handle, tmp_inode);
|
|
|
|
}
|
2008-02-10 14:20:05 +08:00
|
|
|
|
|
|
|
/* We mark the tmp_inode dirty via ext4_ext_tree_init. */
|
2019-11-06 00:44:29 +08:00
|
|
|
retval = ext4_journal_ensure_credits(handle, 1, 0);
|
2019-11-06 00:44:16 +08:00
|
|
|
if (retval < 0)
|
|
|
|
goto out_stop;
|
2008-01-29 12:58:26 +08:00
|
|
|
/*
|
|
|
|
* Mark the tmp_inode as of size zero
|
|
|
|
*/
|
|
|
|
i_size_write(tmp_inode, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set the i_blocks count to zero
|
2014-11-26 05:17:29 +08:00
|
|
|
* so that the ext4_evict_inode() does the
|
2008-01-29 12:58:26 +08:00
|
|
|
* right job
|
|
|
|
*
|
|
|
|
* We don't need to take the i_lock because
|
|
|
|
* the inode is not visible to user space.
|
|
|
|
*/
|
|
|
|
tmp_inode->i_blocks = 0;
|
2022-06-17 14:25:15 +08:00
|
|
|
EXT4_I(tmp_inode)->i_csum_seed = tmp_csum_seed;
|
2008-01-29 12:58:26 +08:00
|
|
|
|
|
|
|
/* Reset the extent details */
|
|
|
|
ext4_ext_tree_init(handle, tmp_inode);
|
2019-11-06 00:44:16 +08:00
|
|
|
out_stop:
|
2008-01-29 12:58:26 +08:00
|
|
|
ext4_journal_stop(handle);
|
2020-02-20 02:30:47 +08:00
|
|
|
out_tmp_inode:
|
2009-08-26 10:36:05 +08:00
|
|
|
unlock_new_inode(tmp_inode);
|
2009-02-16 09:02:19 +08:00
|
|
|
iput(tmp_inode);
|
2020-02-20 02:30:47 +08:00
|
|
|
out_unlock:
|
2023-05-04 20:47:23 +08:00
|
|
|
ext4_writepages_up_write(inode->i_sb, alloc_ctx);
|
2008-01-29 12:58:26 +08:00
|
|
|
return retval;
|
|
|
|
}
|
2013-04-11 11:32:52 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Migrate a simple extent-based inode to use the i_blocks[] array
|
|
|
|
*/
|
|
|
|
int ext4_ind_migrate(struct inode *inode)
|
|
|
|
{
|
|
|
|
struct ext4_extent_header *eh;
|
2020-02-20 02:30:47 +08:00
|
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
|
|
struct ext4_super_block *es = sbi->s_es;
|
2013-04-11 11:32:52 +08:00
|
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
|
|
struct ext4_extent *ex;
|
|
|
|
unsigned int i, len;
|
2015-07-04 12:03:44 +08:00
|
|
|
ext4_lblk_t start, end;
|
2013-04-11 11:32:52 +08:00
|
|
|
ext4_fsblk_t blk;
|
|
|
|
handle_t *handle;
|
2020-04-27 09:34:37 +08:00
|
|
|
int ret, ret2 = 0;
|
2023-05-04 20:47:23 +08:00
|
|
|
int alloc_ctx;
|
2013-04-11 11:32:52 +08:00
|
|
|
|
2015-10-18 04:18:43 +08:00
|
|
|
if (!ext4_has_feature_extents(inode->i_sb) ||
|
2013-04-11 11:32:52 +08:00
|
|
|
(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-10-18 04:18:43 +08:00
|
|
|
if (ext4_has_feature_bigalloc(inode->i_sb))
|
2013-04-11 22:54:46 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2015-07-04 11:56:50 +08:00
|
|
|
/*
|
|
|
|
* In order to get correct extent info, force all delayed allocation
|
|
|
|
* blocks to be allocated, otherwise delayed allocation blocks may not
|
|
|
|
* be reflected and bypass the checks on extent header.
|
|
|
|
*/
|
|
|
|
if (test_opt(inode->i_sb, DELALLOC))
|
|
|
|
ext4_alloc_da_blocks(inode);
|
|
|
|
|
2023-05-04 20:47:23 +08:00
|
|
|
alloc_ctx = ext4_writepages_down_write(inode->i_sb);
|
2020-02-20 02:30:47 +08:00
|
|
|
|
2013-04-11 11:32:52 +08:00
|
|
|
handle = ext4_journal_start(inode, EXT4_HT_MIGRATE, 1);
|
2020-02-20 02:30:47 +08:00
|
|
|
if (IS_ERR(handle)) {
|
|
|
|
ret = PTR_ERR(handle);
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2013-04-11 11:32:52 +08:00
|
|
|
|
|
|
|
down_write(&EXT4_I(inode)->i_data_sem);
|
|
|
|
ret = ext4_ext_check_inode(inode);
|
|
|
|
if (ret)
|
|
|
|
goto errout;
|
|
|
|
|
|
|
|
eh = ext_inode_hdr(inode);
|
|
|
|
ex = EXT_FIRST_EXTENT(eh);
|
|
|
|
if (ext4_blocks_count(es) > EXT4_MAX_BLOCK_FILE_PHYS ||
|
|
|
|
eh->eh_depth != 0 || le16_to_cpu(eh->eh_entries) > 1) {
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
if (eh->eh_entries == 0)
|
2015-07-04 12:03:44 +08:00
|
|
|
blk = len = start = end = 0;
|
2013-04-11 11:32:52 +08:00
|
|
|
else {
|
|
|
|
len = le16_to_cpu(ex->ee_len);
|
|
|
|
blk = ext4_ext_pblock(ex);
|
2015-07-04 12:03:44 +08:00
|
|
|
start = le32_to_cpu(ex->ee_block);
|
|
|
|
end = start + len - 1;
|
2015-07-04 11:56:50 +08:00
|
|
|
if (end >= EXT4_NDIR_BLOCKS) {
|
2013-04-11 11:32:52 +08:00
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
|
|
|
|
memset(ei->i_data, 0, sizeof(ei->i_data));
|
2015-07-04 12:03:44 +08:00
|
|
|
for (i = start; i <= end; i++)
|
2013-04-11 11:32:52 +08:00
|
|
|
ei->i_data[i] = cpu_to_le32(blk++);
|
2020-04-27 09:34:37 +08:00
|
|
|
ret2 = ext4_mark_inode_dirty(handle, inode);
|
|
|
|
if (unlikely(ret2 && !ret))
|
|
|
|
ret = ret2;
|
2013-04-11 11:32:52 +08:00
|
|
|
errout:
|
|
|
|
up_write(&EXT4_I(inode)->i_data_sem);
|
2024-08-29 23:22:09 +08:00
|
|
|
ext4_journal_stop(handle);
|
2020-02-20 02:30:47 +08:00
|
|
|
out_unlock:
|
2023-05-04 20:47:23 +08:00
|
|
|
ext4_writepages_up_write(inode->i_sb, alloc_ctx);
|
2013-04-11 11:32:52 +08:00
|
|
|
return ret;
|
|
|
|
}
|