|
@@ -149,7 +149,7 @@ int ext4_truncate_restart_trans(handle_t *handle, struct inode *inode,
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Drop i_data_sem to avoid deadlock with ext4_get_blocks At this
|
|
|
|
|
|
+ * Drop i_data_sem to avoid deadlock with ext4_map_blocks. At this
|
|
* moment, get_block can be called only for blocks inside i_size since
|
|
* moment, get_block can be called only for blocks inside i_size since
|
|
* page cache has been already dropped and writes are blocked by
|
|
* page cache has been already dropped and writes are blocked by
|
|
* i_mutex. So we can safely drop the i_data_sem here.
|
|
* i_mutex. So we can safely drop the i_data_sem here.
|
|
@@ -348,9 +348,8 @@ static int __ext4_check_blockref(const char *function, struct inode *inode,
|
|
if (blk &&
|
|
if (blk &&
|
|
unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
|
|
unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
|
|
blk, 1))) {
|
|
blk, 1))) {
|
|
- __ext4_error(inode->i_sb, function,
|
|
|
|
- "invalid block reference %u "
|
|
|
|
- "in inode #%lu", blk, inode->i_ino);
|
|
|
|
|
|
+ ext4_error_inode(function, inode,
|
|
|
|
+ "invalid block reference %u", blk);
|
|
return -EIO;
|
|
return -EIO;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -785,7 +784,7 @@ failed:
|
|
/* Allocation failed, free what we already allocated */
|
|
/* Allocation failed, free what we already allocated */
|
|
ext4_free_blocks(handle, inode, 0, new_blocks[0], 1, 0);
|
|
ext4_free_blocks(handle, inode, 0, new_blocks[0], 1, 0);
|
|
for (i = 1; i <= n ; i++) {
|
|
for (i = 1; i <= n ; i++) {
|
|
- /*
|
|
|
|
|
|
+ /*
|
|
* branch[i].bh is newly allocated, so there is no
|
|
* branch[i].bh is newly allocated, so there is no
|
|
* need to revoke the block, which is why we don't
|
|
* need to revoke the block, which is why we don't
|
|
* need to set EXT4_FREE_BLOCKS_METADATA.
|
|
* need to set EXT4_FREE_BLOCKS_METADATA.
|
|
@@ -875,7 +874,7 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode,
|
|
|
|
|
|
err_out:
|
|
err_out:
|
|
for (i = 1; i <= num; i++) {
|
|
for (i = 1; i <= num; i++) {
|
|
- /*
|
|
|
|
|
|
+ /*
|
|
* branch[i].bh is newly allocated, so there is no
|
|
* branch[i].bh is newly allocated, so there is no
|
|
* need to revoke the block, which is why we don't
|
|
* need to revoke the block, which is why we don't
|
|
* need to set EXT4_FREE_BLOCKS_METADATA.
|
|
* need to set EXT4_FREE_BLOCKS_METADATA.
|
|
@@ -890,9 +889,9 @@ err_out:
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * The ext4_ind_get_blocks() function handles non-extents inodes
|
|
|
|
|
|
+ * The ext4_ind_map_blocks() function handles non-extents inodes
|
|
* (i.e., using the traditional indirect/double-indirect i_blocks
|
|
* (i.e., using the traditional indirect/double-indirect i_blocks
|
|
- * scheme) for ext4_get_blocks().
|
|
|
|
|
|
+ * scheme) for ext4_map_blocks().
|
|
*
|
|
*
|
|
* Allocation strategy is simple: if we have to allocate something, we will
|
|
* Allocation strategy is simple: if we have to allocate something, we will
|
|
* have to go the whole way to leaf. So let's do it before attaching anything
|
|
* have to go the whole way to leaf. So let's do it before attaching anything
|
|
@@ -917,9 +916,8 @@ err_out:
|
|
* down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system
|
|
* down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system
|
|
* blocks.
|
|
* blocks.
|
|
*/
|
|
*/
|
|
-static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode,
|
|
|
|
- ext4_lblk_t iblock, unsigned int maxblocks,
|
|
|
|
- struct buffer_head *bh_result,
|
|
|
|
|
|
+static int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
|
|
|
|
+ struct ext4_map_blocks *map,
|
|
int flags)
|
|
int flags)
|
|
{
|
|
{
|
|
int err = -EIO;
|
|
int err = -EIO;
|
|
@@ -933,9 +931,9 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode,
|
|
int count = 0;
|
|
int count = 0;
|
|
ext4_fsblk_t first_block = 0;
|
|
ext4_fsblk_t first_block = 0;
|
|
|
|
|
|
- J_ASSERT(!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL));
|
|
|
|
|
|
+ J_ASSERT(!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)));
|
|
J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0);
|
|
J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0);
|
|
- depth = ext4_block_to_path(inode, iblock, offsets,
|
|
|
|
|
|
+ depth = ext4_block_to_path(inode, map->m_lblk, offsets,
|
|
&blocks_to_boundary);
|
|
&blocks_to_boundary);
|
|
|
|
|
|
if (depth == 0)
|
|
if (depth == 0)
|
|
@@ -946,10 +944,9 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode,
|
|
/* Simplest case - block found, no allocation needed */
|
|
/* Simplest case - block found, no allocation needed */
|
|
if (!partial) {
|
|
if (!partial) {
|
|
first_block = le32_to_cpu(chain[depth - 1].key);
|
|
first_block = le32_to_cpu(chain[depth - 1].key);
|
|
- clear_buffer_new(bh_result);
|
|
|
|
count++;
|
|
count++;
|
|
/*map more blocks*/
|
|
/*map more blocks*/
|
|
- while (count < maxblocks && count <= blocks_to_boundary) {
|
|
|
|
|
|
+ while (count < map->m_len && count <= blocks_to_boundary) {
|
|
ext4_fsblk_t blk;
|
|
ext4_fsblk_t blk;
|
|
|
|
|
|
blk = le32_to_cpu(*(chain[depth-1].p + count));
|
|
blk = le32_to_cpu(*(chain[depth-1].p + count));
|
|
@@ -969,7 +966,7 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode,
|
|
/*
|
|
/*
|
|
* Okay, we need to do block allocation.
|
|
* Okay, we need to do block allocation.
|
|
*/
|
|
*/
|
|
- goal = ext4_find_goal(inode, iblock, partial);
|
|
|
|
|
|
+ goal = ext4_find_goal(inode, map->m_lblk, partial);
|
|
|
|
|
|
/* the number of blocks need to allocate for [d,t]indirect blocks */
|
|
/* the number of blocks need to allocate for [d,t]indirect blocks */
|
|
indirect_blks = (chain + depth) - partial - 1;
|
|
indirect_blks = (chain + depth) - partial - 1;
|
|
@@ -979,11 +976,11 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode,
|
|
* direct blocks to allocate for this branch.
|
|
* direct blocks to allocate for this branch.
|
|
*/
|
|
*/
|
|
count = ext4_blks_to_allocate(partial, indirect_blks,
|
|
count = ext4_blks_to_allocate(partial, indirect_blks,
|
|
- maxblocks, blocks_to_boundary);
|
|
|
|
|
|
+ map->m_len, blocks_to_boundary);
|
|
/*
|
|
/*
|
|
* Block out ext4_truncate while we alter the tree
|
|
* Block out ext4_truncate while we alter the tree
|
|
*/
|
|
*/
|
|
- err = ext4_alloc_branch(handle, inode, iblock, indirect_blks,
|
|
|
|
|
|
+ err = ext4_alloc_branch(handle, inode, map->m_lblk, indirect_blks,
|
|
&count, goal,
|
|
&count, goal,
|
|
offsets + (partial - chain), partial);
|
|
offsets + (partial - chain), partial);
|
|
|
|
|
|
@@ -995,18 +992,20 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode,
|
|
* may need to return -EAGAIN upwards in the worst case. --sct
|
|
* may need to return -EAGAIN upwards in the worst case. --sct
|
|
*/
|
|
*/
|
|
if (!err)
|
|
if (!err)
|
|
- err = ext4_splice_branch(handle, inode, iblock,
|
|
|
|
|
|
+ err = ext4_splice_branch(handle, inode, map->m_lblk,
|
|
partial, indirect_blks, count);
|
|
partial, indirect_blks, count);
|
|
if (err)
|
|
if (err)
|
|
goto cleanup;
|
|
goto cleanup;
|
|
|
|
|
|
- set_buffer_new(bh_result);
|
|
|
|
|
|
+ map->m_flags |= EXT4_MAP_NEW;
|
|
|
|
|
|
ext4_update_inode_fsync_trans(handle, inode, 1);
|
|
ext4_update_inode_fsync_trans(handle, inode, 1);
|
|
got_it:
|
|
got_it:
|
|
- map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));
|
|
|
|
|
|
+ map->m_flags |= EXT4_MAP_MAPPED;
|
|
|
|
+ map->m_pblk = le32_to_cpu(chain[depth-1].key);
|
|
|
|
+ map->m_len = count;
|
|
if (count > blocks_to_boundary)
|
|
if (count > blocks_to_boundary)
|
|
- set_buffer_boundary(bh_result);
|
|
|
|
|
|
+ map->m_flags |= EXT4_MAP_BOUNDARY;
|
|
err = count;
|
|
err = count;
|
|
/* Clean up and exit */
|
|
/* Clean up and exit */
|
|
partial = chain + depth - 1; /* the whole chain */
|
|
partial = chain + depth - 1; /* the whole chain */
|
|
@@ -1016,7 +1015,6 @@ cleanup:
|
|
brelse(partial->bh);
|
|
brelse(partial->bh);
|
|
partial--;
|
|
partial--;
|
|
}
|
|
}
|
|
- BUFFER_TRACE(bh_result, "returned");
|
|
|
|
out:
|
|
out:
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -1061,7 +1059,7 @@ static int ext4_indirect_calc_metadata_amount(struct inode *inode,
|
|
*/
|
|
*/
|
|
static int ext4_calc_metadata_amount(struct inode *inode, sector_t lblock)
|
|
static int ext4_calc_metadata_amount(struct inode *inode, sector_t lblock)
|
|
{
|
|
{
|
|
- if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)
|
|
|
|
|
|
+ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
|
|
return ext4_ext_calc_metadata_amount(inode, lblock);
|
|
return ext4_ext_calc_metadata_amount(inode, lblock);
|
|
|
|
|
|
return ext4_indirect_calc_metadata_amount(inode, lblock);
|
|
return ext4_indirect_calc_metadata_amount(inode, lblock);
|
|
@@ -1076,7 +1074,6 @@ void ext4_da_update_reserve_space(struct inode *inode,
|
|
{
|
|
{
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
- int mdb_free = 0, allocated_meta_blocks = 0;
|
|
|
|
|
|
|
|
spin_lock(&ei->i_block_reservation_lock);
|
|
spin_lock(&ei->i_block_reservation_lock);
|
|
trace_ext4_da_update_reserve_space(inode, used);
|
|
trace_ext4_da_update_reserve_space(inode, used);
|
|
@@ -1091,11 +1088,10 @@ void ext4_da_update_reserve_space(struct inode *inode,
|
|
|
|
|
|
/* Update per-inode reservations */
|
|
/* Update per-inode reservations */
|
|
ei->i_reserved_data_blocks -= used;
|
|
ei->i_reserved_data_blocks -= used;
|
|
- used += ei->i_allocated_meta_blocks;
|
|
|
|
ei->i_reserved_meta_blocks -= ei->i_allocated_meta_blocks;
|
|
ei->i_reserved_meta_blocks -= ei->i_allocated_meta_blocks;
|
|
- allocated_meta_blocks = ei->i_allocated_meta_blocks;
|
|
|
|
|
|
+ percpu_counter_sub(&sbi->s_dirtyblocks_counter,
|
|
|
|
+ used + ei->i_allocated_meta_blocks);
|
|
ei->i_allocated_meta_blocks = 0;
|
|
ei->i_allocated_meta_blocks = 0;
|
|
- percpu_counter_sub(&sbi->s_dirtyblocks_counter, used);
|
|
|
|
|
|
|
|
if (ei->i_reserved_data_blocks == 0) {
|
|
if (ei->i_reserved_data_blocks == 0) {
|
|
/*
|
|
/*
|
|
@@ -1103,30 +1099,23 @@ void ext4_da_update_reserve_space(struct inode *inode,
|
|
* only when we have written all of the delayed
|
|
* only when we have written all of the delayed
|
|
* allocation blocks.
|
|
* allocation blocks.
|
|
*/
|
|
*/
|
|
- mdb_free = ei->i_reserved_meta_blocks;
|
|
|
|
|
|
+ percpu_counter_sub(&sbi->s_dirtyblocks_counter,
|
|
|
|
+ ei->i_reserved_meta_blocks);
|
|
ei->i_reserved_meta_blocks = 0;
|
|
ei->i_reserved_meta_blocks = 0;
|
|
ei->i_da_metadata_calc_len = 0;
|
|
ei->i_da_metadata_calc_len = 0;
|
|
- percpu_counter_sub(&sbi->s_dirtyblocks_counter, mdb_free);
|
|
|
|
}
|
|
}
|
|
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
|
|
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
|
|
|
|
|
|
- /* Update quota subsystem */
|
|
|
|
- if (quota_claim) {
|
|
|
|
|
|
+ /* Update quota subsystem for data blocks */
|
|
|
|
+ if (quota_claim)
|
|
dquot_claim_block(inode, used);
|
|
dquot_claim_block(inode, used);
|
|
- if (mdb_free)
|
|
|
|
- dquot_release_reservation_block(inode, mdb_free);
|
|
|
|
- } else {
|
|
|
|
|
|
+ else {
|
|
/*
|
|
/*
|
|
* We did fallocate with an offset that is already delayed
|
|
* We did fallocate with an offset that is already delayed
|
|
* allocated. So on delayed allocated writeback we should
|
|
* allocated. So on delayed allocated writeback we should
|
|
- * not update the quota for allocated blocks. But then
|
|
|
|
- * converting an fallocate region to initialized region would
|
|
|
|
- * have caused a metadata allocation. So claim quota for
|
|
|
|
- * that
|
|
|
|
|
|
+ * not re-claim the quota for fallocated blocks.
|
|
*/
|
|
*/
|
|
- if (allocated_meta_blocks)
|
|
|
|
- dquot_claim_block(inode, allocated_meta_blocks);
|
|
|
|
- dquot_release_reservation_block(inode, mdb_free + used);
|
|
|
|
|
|
+ dquot_release_reservation_block(inode, used);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1139,15 +1128,15 @@ void ext4_da_update_reserve_space(struct inode *inode,
|
|
ext4_discard_preallocations(inode);
|
|
ext4_discard_preallocations(inode);
|
|
}
|
|
}
|
|
|
|
|
|
-static int check_block_validity(struct inode *inode, const char *msg,
|
|
|
|
- sector_t logical, sector_t phys, int len)
|
|
|
|
|
|
+static int check_block_validity(struct inode *inode, const char *func,
|
|
|
|
+ struct ext4_map_blocks *map)
|
|
{
|
|
{
|
|
- if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), phys, len)) {
|
|
|
|
- __ext4_error(inode->i_sb, msg,
|
|
|
|
- "inode #%lu logical block %llu mapped to %llu "
|
|
|
|
- "(size %d)", inode->i_ino,
|
|
|
|
- (unsigned long long) logical,
|
|
|
|
- (unsigned long long) phys, len);
|
|
|
|
|
|
+ if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk,
|
|
|
|
+ map->m_len)) {
|
|
|
|
+ ext4_error_inode(func, inode,
|
|
|
|
+ "lblock %lu mapped to illegal pblock %llu "
|
|
|
|
+ "(length %d)", (unsigned long) map->m_lblk,
|
|
|
|
+ map->m_pblk, map->m_len);
|
|
return -EIO;
|
|
return -EIO;
|
|
}
|
|
}
|
|
return 0;
|
|
return 0;
|
|
@@ -1212,15 +1201,15 @@ static pgoff_t ext4_num_dirty_pages(struct inode *inode, pgoff_t idx,
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * The ext4_get_blocks() function tries to look up the requested blocks,
|
|
|
|
|
|
+ * The ext4_map_blocks() function tries to look up the requested blocks,
|
|
* and returns if the blocks are already mapped.
|
|
* and returns if the blocks are already mapped.
|
|
*
|
|
*
|
|
* Otherwise it takes the write lock of the i_data_sem and allocate blocks
|
|
* Otherwise it takes the write lock of the i_data_sem and allocate blocks
|
|
* and store the allocated blocks in the result buffer head and mark it
|
|
* and store the allocated blocks in the result buffer head and mark it
|
|
* mapped.
|
|
* mapped.
|
|
*
|
|
*
|
|
- * If file type is extents based, it will call ext4_ext_get_blocks(),
|
|
|
|
- * Otherwise, call with ext4_ind_get_blocks() to handle indirect mapping
|
|
|
|
|
|
+ * If file type is extents based, it will call ext4_ext_map_blocks(),
|
|
|
|
+ * Otherwise, call with ext4_ind_map_blocks() to handle indirect mapping
|
|
* based files
|
|
* based files
|
|
*
|
|
*
|
|
* On success, it returns the number of blocks being mapped or allocate.
|
|
* On success, it returns the number of blocks being mapped or allocate.
|
|
@@ -1233,35 +1222,29 @@ static pgoff_t ext4_num_dirty_pages(struct inode *inode, pgoff_t idx,
|
|
*
|
|
*
|
|
* It returns the error in case of allocation failure.
|
|
* It returns the error in case of allocation failure.
|
|
*/
|
|
*/
|
|
-int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
|
|
|
|
- unsigned int max_blocks, struct buffer_head *bh,
|
|
|
|
- int flags)
|
|
|
|
|
|
+int ext4_map_blocks(handle_t *handle, struct inode *inode,
|
|
|
|
+ struct ext4_map_blocks *map, int flags)
|
|
{
|
|
{
|
|
int retval;
|
|
int retval;
|
|
|
|
|
|
- clear_buffer_mapped(bh);
|
|
|
|
- clear_buffer_unwritten(bh);
|
|
|
|
-
|
|
|
|
- ext_debug("ext4_get_blocks(): inode %lu, flag %d, max_blocks %u,"
|
|
|
|
- "logical block %lu\n", inode->i_ino, flags, max_blocks,
|
|
|
|
- (unsigned long)block);
|
|
|
|
|
|
+ map->m_flags = 0;
|
|
|
|
+ ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u,"
|
|
|
|
+ "logical block %lu\n", inode->i_ino, flags, map->m_len,
|
|
|
|
+ (unsigned long) map->m_lblk);
|
|
/*
|
|
/*
|
|
* Try to see if we can get the block without requesting a new
|
|
* Try to see if we can get the block without requesting a new
|
|
* file system block.
|
|
* file system block.
|
|
*/
|
|
*/
|
|
down_read((&EXT4_I(inode)->i_data_sem));
|
|
down_read((&EXT4_I(inode)->i_data_sem));
|
|
- if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) {
|
|
|
|
- retval = ext4_ext_get_blocks(handle, inode, block, max_blocks,
|
|
|
|
- bh, 0);
|
|
|
|
|
|
+ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
|
|
|
|
+ retval = ext4_ext_map_blocks(handle, inode, map, 0);
|
|
} else {
|
|
} else {
|
|
- retval = ext4_ind_get_blocks(handle, inode, block, max_blocks,
|
|
|
|
- bh, 0);
|
|
|
|
|
|
+ retval = ext4_ind_map_blocks(handle, inode, map, 0);
|
|
}
|
|
}
|
|
up_read((&EXT4_I(inode)->i_data_sem));
|
|
up_read((&EXT4_I(inode)->i_data_sem));
|
|
|
|
|
|
- if (retval > 0 && buffer_mapped(bh)) {
|
|
|
|
- int ret = check_block_validity(inode, "file system corruption",
|
|
|
|
- block, bh->b_blocknr, retval);
|
|
|
|
|
|
+ if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
|
|
|
|
+ int ret = check_block_validity(inode, __func__, map);
|
|
if (ret != 0)
|
|
if (ret != 0)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -1277,7 +1260,7 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
|
|
* ext4_ext_get_block() returns th create = 0
|
|
* ext4_ext_get_block() returns th create = 0
|
|
* with buffer head unmapped.
|
|
* with buffer head unmapped.
|
|
*/
|
|
*/
|
|
- if (retval > 0 && buffer_mapped(bh))
|
|
|
|
|
|
+ if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED)
|
|
return retval;
|
|
return retval;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1290,7 +1273,7 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
|
|
* of BH_Unwritten and BH_Mapped flags being simultaneously
|
|
* of BH_Unwritten and BH_Mapped flags being simultaneously
|
|
* set on the buffer_head.
|
|
* set on the buffer_head.
|
|
*/
|
|
*/
|
|
- clear_buffer_unwritten(bh);
|
|
|
|
|
|
+ map->m_flags &= ~EXT4_MAP_UNWRITTEN;
|
|
|
|
|
|
/*
|
|
/*
|
|
* New blocks allocate and/or writing to uninitialized extent
|
|
* New blocks allocate and/or writing to uninitialized extent
|
|
@@ -1312,14 +1295,12 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
|
|
* We need to check for EXT4 here because migrate
|
|
* We need to check for EXT4 here because migrate
|
|
* could have changed the inode type in between
|
|
* could have changed the inode type in between
|
|
*/
|
|
*/
|
|
- if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) {
|
|
|
|
- retval = ext4_ext_get_blocks(handle, inode, block, max_blocks,
|
|
|
|
- bh, flags);
|
|
|
|
|
|
+ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
|
|
|
|
+ retval = ext4_ext_map_blocks(handle, inode, map, flags);
|
|
} else {
|
|
} else {
|
|
- retval = ext4_ind_get_blocks(handle, inode, block,
|
|
|
|
- max_blocks, bh, flags);
|
|
|
|
|
|
+ retval = ext4_ind_map_blocks(handle, inode, map, flags);
|
|
|
|
|
|
- if (retval > 0 && buffer_new(bh)) {
|
|
|
|
|
|
+ if (retval > 0 && map->m_flags & EXT4_MAP_NEW) {
|
|
/*
|
|
/*
|
|
* We allocated new blocks which will result in
|
|
* We allocated new blocks which will result in
|
|
* i_data's format changing. Force the migrate
|
|
* i_data's format changing. Force the migrate
|
|
@@ -1342,10 +1323,10 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
|
|
EXT4_I(inode)->i_delalloc_reserved_flag = 0;
|
|
EXT4_I(inode)->i_delalloc_reserved_flag = 0;
|
|
|
|
|
|
up_write((&EXT4_I(inode)->i_data_sem));
|
|
up_write((&EXT4_I(inode)->i_data_sem));
|
|
- if (retval > 0 && buffer_mapped(bh)) {
|
|
|
|
- int ret = check_block_validity(inode, "file system "
|
|
|
|
- "corruption after allocation",
|
|
|
|
- block, bh->b_blocknr, retval);
|
|
|
|
|
|
+ if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
|
|
|
|
+ int ret = check_block_validity(inode,
|
|
|
|
+ "ext4_map_blocks_after_alloc",
|
|
|
|
+ map);
|
|
if (ret != 0)
|
|
if (ret != 0)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -1355,109 +1336,109 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
|
|
/* Maximum number of blocks we map for direct IO at once. */
|
|
/* Maximum number of blocks we map for direct IO at once. */
|
|
#define DIO_MAX_BLOCKS 4096
|
|
#define DIO_MAX_BLOCKS 4096
|
|
|
|
|
|
-int ext4_get_block(struct inode *inode, sector_t iblock,
|
|
|
|
- struct buffer_head *bh_result, int create)
|
|
|
|
|
|
+static int _ext4_get_block(struct inode *inode, sector_t iblock,
|
|
|
|
+ struct buffer_head *bh, int flags)
|
|
{
|
|
{
|
|
handle_t *handle = ext4_journal_current_handle();
|
|
handle_t *handle = ext4_journal_current_handle();
|
|
|
|
+ struct ext4_map_blocks map;
|
|
int ret = 0, started = 0;
|
|
int ret = 0, started = 0;
|
|
- unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
|
|
|
|
int dio_credits;
|
|
int dio_credits;
|
|
|
|
|
|
- if (create && !handle) {
|
|
|
|
|
|
+ map.m_lblk = iblock;
|
|
|
|
+ map.m_len = bh->b_size >> inode->i_blkbits;
|
|
|
|
+
|
|
|
|
+ if (flags && !handle) {
|
|
/* Direct IO write... */
|
|
/* Direct IO write... */
|
|
- if (max_blocks > DIO_MAX_BLOCKS)
|
|
|
|
- max_blocks = DIO_MAX_BLOCKS;
|
|
|
|
- dio_credits = ext4_chunk_trans_blocks(inode, max_blocks);
|
|
|
|
|
|
+ if (map.m_len > DIO_MAX_BLOCKS)
|
|
|
|
+ map.m_len = DIO_MAX_BLOCKS;
|
|
|
|
+ dio_credits = ext4_chunk_trans_blocks(inode, map.m_len);
|
|
handle = ext4_journal_start(inode, dio_credits);
|
|
handle = ext4_journal_start(inode, dio_credits);
|
|
if (IS_ERR(handle)) {
|
|
if (IS_ERR(handle)) {
|
|
ret = PTR_ERR(handle);
|
|
ret = PTR_ERR(handle);
|
|
- goto out;
|
|
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
started = 1;
|
|
started = 1;
|
|
}
|
|
}
|
|
|
|
|
|
- ret = ext4_get_blocks(handle, inode, iblock, max_blocks, bh_result,
|
|
|
|
- create ? EXT4_GET_BLOCKS_CREATE : 0);
|
|
|
|
|
|
+ ret = ext4_map_blocks(handle, inode, &map, flags);
|
|
if (ret > 0) {
|
|
if (ret > 0) {
|
|
- bh_result->b_size = (ret << inode->i_blkbits);
|
|
|
|
|
|
+ map_bh(bh, inode->i_sb, map.m_pblk);
|
|
|
|
+ bh->b_state = (bh->b_state & ~EXT4_MAP_FLAGS) | map.m_flags;
|
|
|
|
+ bh->b_size = inode->i_sb->s_blocksize * map.m_len;
|
|
ret = 0;
|
|
ret = 0;
|
|
}
|
|
}
|
|
if (started)
|
|
if (started)
|
|
ext4_journal_stop(handle);
|
|
ext4_journal_stop(handle);
|
|
-out:
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int ext4_get_block(struct inode *inode, sector_t iblock,
|
|
|
|
+ struct buffer_head *bh, int create)
|
|
|
|
+{
|
|
|
|
+ return _ext4_get_block(inode, iblock, bh,
|
|
|
|
+ create ? EXT4_GET_BLOCKS_CREATE : 0);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* `handle' can be NULL if create is zero
|
|
* `handle' can be NULL if create is zero
|
|
*/
|
|
*/
|
|
struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
|
|
struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
|
|
ext4_lblk_t block, int create, int *errp)
|
|
ext4_lblk_t block, int create, int *errp)
|
|
{
|
|
{
|
|
- struct buffer_head dummy;
|
|
|
|
|
|
+ struct ext4_map_blocks map;
|
|
|
|
+ struct buffer_head *bh;
|
|
int fatal = 0, err;
|
|
int fatal = 0, err;
|
|
- int flags = 0;
|
|
|
|
|
|
|
|
J_ASSERT(handle != NULL || create == 0);
|
|
J_ASSERT(handle != NULL || create == 0);
|
|
|
|
|
|
- dummy.b_state = 0;
|
|
|
|
- dummy.b_blocknr = -1000;
|
|
|
|
- buffer_trace_init(&dummy.b_history);
|
|
|
|
- if (create)
|
|
|
|
- flags |= EXT4_GET_BLOCKS_CREATE;
|
|
|
|
- err = ext4_get_blocks(handle, inode, block, 1, &dummy, flags);
|
|
|
|
- /*
|
|
|
|
- * ext4_get_blocks() returns number of blocks mapped. 0 in
|
|
|
|
- * case of a HOLE.
|
|
|
|
- */
|
|
|
|
- if (err > 0) {
|
|
|
|
- if (err > 1)
|
|
|
|
- WARN_ON(1);
|
|
|
|
- err = 0;
|
|
|
|
|
|
+ map.m_lblk = block;
|
|
|
|
+ map.m_len = 1;
|
|
|
|
+ err = ext4_map_blocks(handle, inode, &map,
|
|
|
|
+ create ? EXT4_GET_BLOCKS_CREATE : 0);
|
|
|
|
+
|
|
|
|
+ if (err < 0)
|
|
|
|
+ *errp = err;
|
|
|
|
+ if (err <= 0)
|
|
|
|
+ return NULL;
|
|
|
|
+ *errp = 0;
|
|
|
|
+
|
|
|
|
+ bh = sb_getblk(inode->i_sb, map.m_pblk);
|
|
|
|
+ if (!bh) {
|
|
|
|
+ *errp = -EIO;
|
|
|
|
+ return NULL;
|
|
}
|
|
}
|
|
- *errp = err;
|
|
|
|
- if (!err && buffer_mapped(&dummy)) {
|
|
|
|
- struct buffer_head *bh;
|
|
|
|
- bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
|
|
|
|
- if (!bh) {
|
|
|
|
- *errp = -EIO;
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (buffer_new(&dummy)) {
|
|
|
|
- J_ASSERT(create != 0);
|
|
|
|
- J_ASSERT(handle != NULL);
|
|
|
|
|
|
+ if (map.m_flags & EXT4_MAP_NEW) {
|
|
|
|
+ J_ASSERT(create != 0);
|
|
|
|
+ J_ASSERT(handle != NULL);
|
|
|
|
|
|
- /*
|
|
|
|
- * Now that we do not always journal data, we should
|
|
|
|
- * keep in mind whether this should always journal the
|
|
|
|
- * new buffer as metadata. For now, regular file
|
|
|
|
- * writes use ext4_get_block instead, so it's not a
|
|
|
|
- * problem.
|
|
|
|
- */
|
|
|
|
- lock_buffer(bh);
|
|
|
|
- BUFFER_TRACE(bh, "call get_create_access");
|
|
|
|
- fatal = ext4_journal_get_create_access(handle, bh);
|
|
|
|
- if (!fatal && !buffer_uptodate(bh)) {
|
|
|
|
- memset(bh->b_data, 0, inode->i_sb->s_blocksize);
|
|
|
|
- set_buffer_uptodate(bh);
|
|
|
|
- }
|
|
|
|
- unlock_buffer(bh);
|
|
|
|
- BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
|
|
|
|
- err = ext4_handle_dirty_metadata(handle, inode, bh);
|
|
|
|
- if (!fatal)
|
|
|
|
- fatal = err;
|
|
|
|
- } else {
|
|
|
|
- BUFFER_TRACE(bh, "not a new buffer");
|
|
|
|
- }
|
|
|
|
- if (fatal) {
|
|
|
|
- *errp = fatal;
|
|
|
|
- brelse(bh);
|
|
|
|
- bh = NULL;
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Now that we do not always journal data, we should
|
|
|
|
+ * keep in mind whether this should always journal the
|
|
|
|
+ * new buffer as metadata. For now, regular file
|
|
|
|
+ * writes use ext4_get_block instead, so it's not a
|
|
|
|
+ * problem.
|
|
|
|
+ */
|
|
|
|
+ lock_buffer(bh);
|
|
|
|
+ BUFFER_TRACE(bh, "call get_create_access");
|
|
|
|
+ fatal = ext4_journal_get_create_access(handle, bh);
|
|
|
|
+ if (!fatal && !buffer_uptodate(bh)) {
|
|
|
|
+ memset(bh->b_data, 0, inode->i_sb->s_blocksize);
|
|
|
|
+ set_buffer_uptodate(bh);
|
|
}
|
|
}
|
|
- return bh;
|
|
|
|
|
|
+ unlock_buffer(bh);
|
|
|
|
+ BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
|
|
|
|
+ err = ext4_handle_dirty_metadata(handle, inode, bh);
|
|
|
|
+ if (!fatal)
|
|
|
|
+ fatal = err;
|
|
|
|
+ } else {
|
|
|
|
+ BUFFER_TRACE(bh, "not a new buffer");
|
|
}
|
|
}
|
|
-err:
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ if (fatal) {
|
|
|
|
+ *errp = fatal;
|
|
|
|
+ brelse(bh);
|
|
|
|
+ bh = NULL;
|
|
|
|
+ }
|
|
|
|
+ return bh;
|
|
}
|
|
}
|
|
|
|
|
|
struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
|
|
struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
|
|
@@ -1860,7 +1841,7 @@ static int ext4_da_reserve_space(struct inode *inode, sector_t lblock)
|
|
int retries = 0;
|
|
int retries = 0;
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
struct ext4_inode_info *ei = EXT4_I(inode);
|
|
- unsigned long md_needed, md_reserved;
|
|
|
|
|
|
+ unsigned long md_needed;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1870,22 +1851,24 @@ static int ext4_da_reserve_space(struct inode *inode, sector_t lblock)
|
|
*/
|
|
*/
|
|
repeat:
|
|
repeat:
|
|
spin_lock(&ei->i_block_reservation_lock);
|
|
spin_lock(&ei->i_block_reservation_lock);
|
|
- md_reserved = ei->i_reserved_meta_blocks;
|
|
|
|
md_needed = ext4_calc_metadata_amount(inode, lblock);
|
|
md_needed = ext4_calc_metadata_amount(inode, lblock);
|
|
trace_ext4_da_reserve_space(inode, md_needed);
|
|
trace_ext4_da_reserve_space(inode, md_needed);
|
|
spin_unlock(&ei->i_block_reservation_lock);
|
|
spin_unlock(&ei->i_block_reservation_lock);
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Make quota reservation here to prevent quota overflow
|
|
|
|
- * later. Real quota accounting is done at pages writeout
|
|
|
|
- * time.
|
|
|
|
|
|
+ * We will charge metadata quota at writeout time; this saves
|
|
|
|
+ * us from metadata over-estimation, though we may go over by
|
|
|
|
+ * a small amount in the end. Here we just reserve for data.
|
|
*/
|
|
*/
|
|
- ret = dquot_reserve_block(inode, md_needed + 1);
|
|
|
|
|
|
+ ret = dquot_reserve_block(inode, 1);
|
|
if (ret)
|
|
if (ret)
|
|
return ret;
|
|
return ret;
|
|
-
|
|
|
|
|
|
+ /*
|
|
|
|
+ * We do still charge estimated metadata to the sb though;
|
|
|
|
+ * we cannot afford to run out of free blocks.
|
|
|
|
+ */
|
|
if (ext4_claim_free_blocks(sbi, md_needed + 1)) {
|
|
if (ext4_claim_free_blocks(sbi, md_needed + 1)) {
|
|
- dquot_release_reservation_block(inode, md_needed + 1);
|
|
|
|
|
|
+ dquot_release_reservation_block(inode, 1);
|
|
if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
|
|
if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
|
|
yield();
|
|
yield();
|
|
goto repeat;
|
|
goto repeat;
|
|
@@ -1910,6 +1893,7 @@ static void ext4_da_release_space(struct inode *inode, int to_free)
|
|
|
|
|
|
spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
|
|
spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
|
|
|
|
|
|
|
|
+ trace_ext4_da_release_space(inode, to_free);
|
|
if (unlikely(to_free > ei->i_reserved_data_blocks)) {
|
|
if (unlikely(to_free > ei->i_reserved_data_blocks)) {
|
|
/*
|
|
/*
|
|
* if there aren't enough reserved blocks, then the
|
|
* if there aren't enough reserved blocks, then the
|
|
@@ -1932,12 +1916,13 @@ static void ext4_da_release_space(struct inode *inode, int to_free)
|
|
* only when we have written all of the delayed
|
|
* only when we have written all of the delayed
|
|
* allocation blocks.
|
|
* allocation blocks.
|
|
*/
|
|
*/
|
|
- to_free += ei->i_reserved_meta_blocks;
|
|
|
|
|
|
+ percpu_counter_sub(&sbi->s_dirtyblocks_counter,
|
|
|
|
+ ei->i_reserved_meta_blocks);
|
|
ei->i_reserved_meta_blocks = 0;
|
|
ei->i_reserved_meta_blocks = 0;
|
|
ei->i_da_metadata_calc_len = 0;
|
|
ei->i_da_metadata_calc_len = 0;
|
|
}
|
|
}
|
|
|
|
|
|
- /* update fs dirty blocks counter */
|
|
|
|
|
|
+ /* update fs dirty data blocks counter */
|
|
percpu_counter_sub(&sbi->s_dirtyblocks_counter, to_free);
|
|
percpu_counter_sub(&sbi->s_dirtyblocks_counter, to_free);
|
|
|
|
|
|
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
|
|
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
|
|
@@ -2042,28 +2027,23 @@ static int mpage_da_submit_io(struct mpage_da_data *mpd)
|
|
/*
|
|
/*
|
|
* mpage_put_bnr_to_bhs - walk blocks and assign them actual numbers
|
|
* mpage_put_bnr_to_bhs - walk blocks and assign them actual numbers
|
|
*
|
|
*
|
|
- * @mpd->inode - inode to walk through
|
|
|
|
- * @exbh->b_blocknr - first block on a disk
|
|
|
|
- * @exbh->b_size - amount of space in bytes
|
|
|
|
- * @logical - first logical block to start assignment with
|
|
|
|
- *
|
|
|
|
* the function goes through all passed space and put actual disk
|
|
* the function goes through all passed space and put actual disk
|
|
* block numbers into buffer heads, dropping BH_Delay and BH_Unwritten
|
|
* block numbers into buffer heads, dropping BH_Delay and BH_Unwritten
|
|
*/
|
|
*/
|
|
-static void mpage_put_bnr_to_bhs(struct mpage_da_data *mpd, sector_t logical,
|
|
|
|
- struct buffer_head *exbh)
|
|
|
|
|
|
+static void mpage_put_bnr_to_bhs(struct mpage_da_data *mpd,
|
|
|
|
+ struct ext4_map_blocks *map)
|
|
{
|
|
{
|
|
struct inode *inode = mpd->inode;
|
|
struct inode *inode = mpd->inode;
|
|
struct address_space *mapping = inode->i_mapping;
|
|
struct address_space *mapping = inode->i_mapping;
|
|
- int blocks = exbh->b_size >> inode->i_blkbits;
|
|
|
|
- sector_t pblock = exbh->b_blocknr, cur_logical;
|
|
|
|
|
|
+ int blocks = map->m_len;
|
|
|
|
+ sector_t pblock = map->m_pblk, cur_logical;
|
|
struct buffer_head *head, *bh;
|
|
struct buffer_head *head, *bh;
|
|
pgoff_t index, end;
|
|
pgoff_t index, end;
|
|
struct pagevec pvec;
|
|
struct pagevec pvec;
|
|
int nr_pages, i;
|
|
int nr_pages, i;
|
|
|
|
|
|
- index = logical >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
|
|
|
- end = (logical + blocks - 1) >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
|
|
|
|
|
+ index = map->m_lblk >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
|
|
|
+ end = (map->m_lblk + blocks - 1) >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
|
cur_logical = index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
|
cur_logical = index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
|
|
|
|
|
pagevec_init(&pvec, 0);
|
|
pagevec_init(&pvec, 0);
|
|
@@ -2090,17 +2070,16 @@ static void mpage_put_bnr_to_bhs(struct mpage_da_data *mpd, sector_t logical,
|
|
|
|
|
|
/* skip blocks out of the range */
|
|
/* skip blocks out of the range */
|
|
do {
|
|
do {
|
|
- if (cur_logical >= logical)
|
|
|
|
|
|
+ if (cur_logical >= map->m_lblk)
|
|
break;
|
|
break;
|
|
cur_logical++;
|
|
cur_logical++;
|
|
} while ((bh = bh->b_this_page) != head);
|
|
} while ((bh = bh->b_this_page) != head);
|
|
|
|
|
|
do {
|
|
do {
|
|
- if (cur_logical >= logical + blocks)
|
|
|
|
|
|
+ if (cur_logical >= map->m_lblk + blocks)
|
|
break;
|
|
break;
|
|
|
|
|
|
- if (buffer_delay(bh) ||
|
|
|
|
- buffer_unwritten(bh)) {
|
|
|
|
|
|
+ if (buffer_delay(bh) || buffer_unwritten(bh)) {
|
|
|
|
|
|
BUG_ON(bh->b_bdev != inode->i_sb->s_bdev);
|
|
BUG_ON(bh->b_bdev != inode->i_sb->s_bdev);
|
|
|
|
|
|
@@ -2119,7 +2098,7 @@ static void mpage_put_bnr_to_bhs(struct mpage_da_data *mpd, sector_t logical,
|
|
} else if (buffer_mapped(bh))
|
|
} else if (buffer_mapped(bh))
|
|
BUG_ON(bh->b_blocknr != pblock);
|
|
BUG_ON(bh->b_blocknr != pblock);
|
|
|
|
|
|
- if (buffer_uninit(exbh))
|
|
|
|
|
|
+ if (map->m_flags & EXT4_MAP_UNINIT)
|
|
set_buffer_uninit(bh);
|
|
set_buffer_uninit(bh);
|
|
cur_logical++;
|
|
cur_logical++;
|
|
pblock++;
|
|
pblock++;
|
|
@@ -2130,21 +2109,6 @@ static void mpage_put_bnr_to_bhs(struct mpage_da_data *mpd, sector_t logical,
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-/*
|
|
|
|
- * __unmap_underlying_blocks - just a helper function to unmap
|
|
|
|
- * set of blocks described by @bh
|
|
|
|
- */
|
|
|
|
-static inline void __unmap_underlying_blocks(struct inode *inode,
|
|
|
|
- struct buffer_head *bh)
|
|
|
|
-{
|
|
|
|
- struct block_device *bdev = inode->i_sb->s_bdev;
|
|
|
|
- int blocks, i;
|
|
|
|
-
|
|
|
|
- blocks = bh->b_size >> inode->i_blkbits;
|
|
|
|
- for (i = 0; i < blocks; i++)
|
|
|
|
- unmap_underlying_metadata(bdev, bh->b_blocknr + i);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void ext4_da_block_invalidatepages(struct mpage_da_data *mpd,
|
|
static void ext4_da_block_invalidatepages(struct mpage_da_data *mpd,
|
|
sector_t logical, long blk_cnt)
|
|
sector_t logical, long blk_cnt)
|
|
{
|
|
{
|
|
@@ -2206,7 +2170,7 @@ static void ext4_print_free_blocks(struct inode *inode)
|
|
static int mpage_da_map_blocks(struct mpage_da_data *mpd)
|
|
static int mpage_da_map_blocks(struct mpage_da_data *mpd)
|
|
{
|
|
{
|
|
int err, blks, get_blocks_flags;
|
|
int err, blks, get_blocks_flags;
|
|
- struct buffer_head new;
|
|
|
|
|
|
+ struct ext4_map_blocks map;
|
|
sector_t next = mpd->b_blocknr;
|
|
sector_t next = mpd->b_blocknr;
|
|
unsigned max_blocks = mpd->b_size >> mpd->inode->i_blkbits;
|
|
unsigned max_blocks = mpd->b_size >> mpd->inode->i_blkbits;
|
|
loff_t disksize = EXT4_I(mpd->inode)->i_disksize;
|
|
loff_t disksize = EXT4_I(mpd->inode)->i_disksize;
|
|
@@ -2247,15 +2211,15 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd)
|
|
* EXT4_GET_BLOCKS_DELALLOC_RESERVE so the delalloc accounting
|
|
* EXT4_GET_BLOCKS_DELALLOC_RESERVE so the delalloc accounting
|
|
* variables are updated after the blocks have been allocated.
|
|
* variables are updated after the blocks have been allocated.
|
|
*/
|
|
*/
|
|
- new.b_state = 0;
|
|
|
|
|
|
+ map.m_lblk = next;
|
|
|
|
+ map.m_len = max_blocks;
|
|
get_blocks_flags = EXT4_GET_BLOCKS_CREATE;
|
|
get_blocks_flags = EXT4_GET_BLOCKS_CREATE;
|
|
if (ext4_should_dioread_nolock(mpd->inode))
|
|
if (ext4_should_dioread_nolock(mpd->inode))
|
|
get_blocks_flags |= EXT4_GET_BLOCKS_IO_CREATE_EXT;
|
|
get_blocks_flags |= EXT4_GET_BLOCKS_IO_CREATE_EXT;
|
|
if (mpd->b_state & (1 << BH_Delay))
|
|
if (mpd->b_state & (1 << BH_Delay))
|
|
get_blocks_flags |= EXT4_GET_BLOCKS_DELALLOC_RESERVE;
|
|
get_blocks_flags |= EXT4_GET_BLOCKS_DELALLOC_RESERVE;
|
|
|
|
|
|
- blks = ext4_get_blocks(handle, mpd->inode, next, max_blocks,
|
|
|
|
- &new, get_blocks_flags);
|
|
|
|
|
|
+ blks = ext4_map_blocks(handle, mpd->inode, &map, get_blocks_flags);
|
|
if (blks < 0) {
|
|
if (blks < 0) {
|
|
err = blks;
|
|
err = blks;
|
|
/*
|
|
/*
|
|
@@ -2282,7 +2246,7 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd)
|
|
ext4_msg(mpd->inode->i_sb, KERN_CRIT,
|
|
ext4_msg(mpd->inode->i_sb, KERN_CRIT,
|
|
"delayed block allocation failed for inode %lu at "
|
|
"delayed block allocation failed for inode %lu at "
|
|
"logical offset %llu with max blocks %zd with "
|
|
"logical offset %llu with max blocks %zd with "
|
|
- "error %d\n", mpd->inode->i_ino,
|
|
|
|
|
|
+ "error %d", mpd->inode->i_ino,
|
|
(unsigned long long) next,
|
|
(unsigned long long) next,
|
|
mpd->b_size >> mpd->inode->i_blkbits, err);
|
|
mpd->b_size >> mpd->inode->i_blkbits, err);
|
|
printk(KERN_CRIT "This should not happen!! "
|
|
printk(KERN_CRIT "This should not happen!! "
|
|
@@ -2297,10 +2261,13 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd)
|
|
}
|
|
}
|
|
BUG_ON(blks == 0);
|
|
BUG_ON(blks == 0);
|
|
|
|
|
|
- new.b_size = (blks << mpd->inode->i_blkbits);
|
|
|
|
|
|
+ if (map.m_flags & EXT4_MAP_NEW) {
|
|
|
|
+ struct block_device *bdev = mpd->inode->i_sb->s_bdev;
|
|
|
|
+ int i;
|
|
|
|
|
|
- if (buffer_new(&new))
|
|
|
|
- __unmap_underlying_blocks(mpd->inode, &new);
|
|
|
|
|
|
+ for (i = 0; i < map.m_len; i++)
|
|
|
|
+ unmap_underlying_metadata(bdev, map.m_pblk + i);
|
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
/*
|
|
* If blocks are delayed marked, we need to
|
|
* If blocks are delayed marked, we need to
|
|
@@ -2308,7 +2275,7 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd)
|
|
*/
|
|
*/
|
|
if ((mpd->b_state & (1 << BH_Delay)) ||
|
|
if ((mpd->b_state & (1 << BH_Delay)) ||
|
|
(mpd->b_state & (1 << BH_Unwritten)))
|
|
(mpd->b_state & (1 << BH_Unwritten)))
|
|
- mpage_put_bnr_to_bhs(mpd, next, &new);
|
|
|
|
|
|
+ mpage_put_bnr_to_bhs(mpd, &map);
|
|
|
|
|
|
if (ext4_should_order_data(mpd->inode)) {
|
|
if (ext4_should_order_data(mpd->inode)) {
|
|
err = ext4_jbd2_file_inode(handle, mpd->inode);
|
|
err = ext4_jbd2_file_inode(handle, mpd->inode);
|
|
@@ -2349,8 +2316,17 @@ static void mpage_add_bh_to_extent(struct mpage_da_data *mpd,
|
|
sector_t next;
|
|
sector_t next;
|
|
int nrblocks = mpd->b_size >> mpd->inode->i_blkbits;
|
|
int nrblocks = mpd->b_size >> mpd->inode->i_blkbits;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * XXX Don't go larger than mballoc is willing to allocate
|
|
|
|
+ * This is a stopgap solution. We eventually need to fold
|
|
|
|
+ * mpage_da_submit_io() into this function and then call
|
|
|
|
+ * ext4_get_blocks() multiple times in a loop
|
|
|
|
+ */
|
|
|
|
+ if (nrblocks >= 8*1024*1024/mpd->inode->i_sb->s_blocksize)
|
|
|
|
+ goto flush_it;
|
|
|
|
+
|
|
/* check if thereserved journal credits might overflow */
|
|
/* check if thereserved journal credits might overflow */
|
|
- if (!(EXT4_I(mpd->inode)->i_flags & EXT4_EXTENTS_FL)) {
|
|
|
|
|
|
+ if (!(ext4_test_inode_flag(mpd->inode, EXT4_INODE_EXTENTS))) {
|
|
if (nrblocks >= EXT4_MAX_TRANS_DATA) {
|
|
if (nrblocks >= EXT4_MAX_TRANS_DATA) {
|
|
/*
|
|
/*
|
|
* With non-extent format we are limited by the journal
|
|
* With non-extent format we are limited by the journal
|
|
@@ -2423,17 +2399,6 @@ static int __mpage_da_writepage(struct page *page,
|
|
struct buffer_head *bh, *head;
|
|
struct buffer_head *bh, *head;
|
|
sector_t logical;
|
|
sector_t logical;
|
|
|
|
|
|
- if (mpd->io_done) {
|
|
|
|
- /*
|
|
|
|
- * Rest of the page in the page_vec
|
|
|
|
- * redirty then and skip then. We will
|
|
|
|
- * try to write them again after
|
|
|
|
- * starting a new transaction
|
|
|
|
- */
|
|
|
|
- redirty_page_for_writepage(wbc, page);
|
|
|
|
- unlock_page(page);
|
|
|
|
- return MPAGE_DA_EXTENT_TAIL;
|
|
|
|
- }
|
|
|
|
/*
|
|
/*
|
|
* Can we merge this page to current extent?
|
|
* Can we merge this page to current extent?
|
|
*/
|
|
*/
|
|
@@ -2528,8 +2493,9 @@ static int __mpage_da_writepage(struct page *page,
|
|
* initialized properly.
|
|
* initialized properly.
|
|
*/
|
|
*/
|
|
static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
|
|
static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
|
|
- struct buffer_head *bh_result, int create)
|
|
|
|
|
|
+ struct buffer_head *bh, int create)
|
|
{
|
|
{
|
|
|
|
+ struct ext4_map_blocks map;
|
|
int ret = 0;
|
|
int ret = 0;
|
|
sector_t invalid_block = ~((sector_t) 0xffff);
|
|
sector_t invalid_block = ~((sector_t) 0xffff);
|
|
|
|
|
|
@@ -2537,16 +2503,22 @@ static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
|
|
invalid_block = ~0;
|
|
invalid_block = ~0;
|
|
|
|
|
|
BUG_ON(create == 0);
|
|
BUG_ON(create == 0);
|
|
- BUG_ON(bh_result->b_size != inode->i_sb->s_blocksize);
|
|
|
|
|
|
+ BUG_ON(bh->b_size != inode->i_sb->s_blocksize);
|
|
|
|
+
|
|
|
|
+ map.m_lblk = iblock;
|
|
|
|
+ map.m_len = 1;
|
|
|
|
|
|
/*
|
|
/*
|
|
* first, we need to know whether the block is allocated already
|
|
* first, we need to know whether the block is allocated already
|
|
* preallocated blocks are unmapped but should treated
|
|
* preallocated blocks are unmapped but should treated
|
|
* the same as allocated blocks.
|
|
* the same as allocated blocks.
|
|
*/
|
|
*/
|
|
- ret = ext4_get_blocks(NULL, inode, iblock, 1, bh_result, 0);
|
|
|
|
- if ((ret == 0) && !buffer_delay(bh_result)) {
|
|
|
|
- /* the block isn't (pre)allocated yet, let's reserve space */
|
|
|
|
|
|
+ ret = ext4_map_blocks(NULL, inode, &map, 0);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+ if (ret == 0) {
|
|
|
|
+ if (buffer_delay(bh))
|
|
|
|
+ return 0; /* Not sure this could or should happen */
|
|
/*
|
|
/*
|
|
* XXX: __block_prepare_write() unmaps passed block,
|
|
* XXX: __block_prepare_write() unmaps passed block,
|
|
* is it OK?
|
|
* is it OK?
|
|
@@ -2556,26 +2528,26 @@ static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
|
|
/* not enough space to reserve */
|
|
/* not enough space to reserve */
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
- map_bh(bh_result, inode->i_sb, invalid_block);
|
|
|
|
- set_buffer_new(bh_result);
|
|
|
|
- set_buffer_delay(bh_result);
|
|
|
|
- } else if (ret > 0) {
|
|
|
|
- bh_result->b_size = (ret << inode->i_blkbits);
|
|
|
|
- if (buffer_unwritten(bh_result)) {
|
|
|
|
- /* A delayed write to unwritten bh should
|
|
|
|
- * be marked new and mapped. Mapped ensures
|
|
|
|
- * that we don't do get_block multiple times
|
|
|
|
- * when we write to the same offset and new
|
|
|
|
- * ensures that we do proper zero out for
|
|
|
|
- * partial write.
|
|
|
|
- */
|
|
|
|
- set_buffer_new(bh_result);
|
|
|
|
- set_buffer_mapped(bh_result);
|
|
|
|
- }
|
|
|
|
- ret = 0;
|
|
|
|
|
|
+ map_bh(bh, inode->i_sb, invalid_block);
|
|
|
|
+ set_buffer_new(bh);
|
|
|
|
+ set_buffer_delay(bh);
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- return ret;
|
|
|
|
|
|
+ map_bh(bh, inode->i_sb, map.m_pblk);
|
|
|
|
+ bh->b_state = (bh->b_state & ~EXT4_MAP_FLAGS) | map.m_flags;
|
|
|
|
+
|
|
|
|
+ if (buffer_unwritten(bh)) {
|
|
|
|
+ /* A delayed write to unwritten bh should be marked
|
|
|
|
+ * new and mapped. Mapped ensures that we don't do
|
|
|
|
+ * get_block multiple times when we write to the same
|
|
|
|
+ * offset and new ensures that we do proper zero out
|
|
|
|
+ * for partial write.
|
|
|
|
+ */
|
|
|
|
+ set_buffer_new(bh);
|
|
|
|
+ set_buffer_mapped(bh);
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -2597,21 +2569,8 @@ static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock,
|
|
static int noalloc_get_block_write(struct inode *inode, sector_t iblock,
|
|
static int noalloc_get_block_write(struct inode *inode, sector_t iblock,
|
|
struct buffer_head *bh_result, int create)
|
|
struct buffer_head *bh_result, int create)
|
|
{
|
|
{
|
|
- int ret = 0;
|
|
|
|
- unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
|
|
|
|
-
|
|
|
|
BUG_ON(bh_result->b_size != inode->i_sb->s_blocksize);
|
|
BUG_ON(bh_result->b_size != inode->i_sb->s_blocksize);
|
|
-
|
|
|
|
- /*
|
|
|
|
- * we don't want to do block allocation in writepage
|
|
|
|
- * so call get_block_wrap with create = 0
|
|
|
|
- */
|
|
|
|
- ret = ext4_get_blocks(NULL, inode, iblock, max_blocks, bh_result, 0);
|
|
|
|
- if (ret > 0) {
|
|
|
|
- bh_result->b_size = (ret << inode->i_blkbits);
|
|
|
|
- ret = 0;
|
|
|
|
- }
|
|
|
|
- return ret;
|
|
|
|
|
|
+ return _ext4_get_block(inode, iblock, bh_result, 0);
|
|
}
|
|
}
|
|
|
|
|
|
static int bget_one(handle_t *handle, struct buffer_head *bh)
|
|
static int bget_one(handle_t *handle, struct buffer_head *bh)
|
|
@@ -2821,13 +2780,131 @@ static int ext4_da_writepages_trans_blocks(struct inode *inode)
|
|
* number of contiguous block. So we will limit
|
|
* number of contiguous block. So we will limit
|
|
* number of contiguous block to a sane value
|
|
* number of contiguous block to a sane value
|
|
*/
|
|
*/
|
|
- if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) &&
|
|
|
|
|
|
+ if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) &&
|
|
(max_blocks > EXT4_MAX_TRANS_DATA))
|
|
(max_blocks > EXT4_MAX_TRANS_DATA))
|
|
max_blocks = EXT4_MAX_TRANS_DATA;
|
|
max_blocks = EXT4_MAX_TRANS_DATA;
|
|
|
|
|
|
return ext4_chunk_trans_blocks(inode, max_blocks);
|
|
return ext4_chunk_trans_blocks(inode, max_blocks);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * write_cache_pages_da - walk the list of dirty pages of the given
|
|
|
|
+ * address space and call the callback function (which usually writes
|
|
|
|
+ * the pages).
|
|
|
|
+ *
|
|
|
|
+ * This is a forked version of write_cache_pages(). Differences:
|
|
|
|
+ * Range cyclic is ignored.
|
|
|
|
+ * no_nrwrite_index_update is always presumed true
|
|
|
|
+ */
|
|
|
|
+static int write_cache_pages_da(struct address_space *mapping,
|
|
|
|
+ struct writeback_control *wbc,
|
|
|
|
+ struct mpage_da_data *mpd)
|
|
|
|
+{
|
|
|
|
+ int ret = 0;
|
|
|
|
+ int done = 0;
|
|
|
|
+ struct pagevec pvec;
|
|
|
|
+ int nr_pages;
|
|
|
|
+ pgoff_t index;
|
|
|
|
+ pgoff_t end; /* Inclusive */
|
|
|
|
+ long nr_to_write = wbc->nr_to_write;
|
|
|
|
+
|
|
|
|
+ pagevec_init(&pvec, 0);
|
|
|
|
+ index = wbc->range_start >> PAGE_CACHE_SHIFT;
|
|
|
|
+ end = wbc->range_end >> PAGE_CACHE_SHIFT;
|
|
|
|
+
|
|
|
|
+ while (!done && (index <= end)) {
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
|
|
|
|
+ PAGECACHE_TAG_DIRTY,
|
|
|
|
+ min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1);
|
|
|
|
+ if (nr_pages == 0)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < nr_pages; i++) {
|
|
|
|
+ struct page *page = pvec.pages[i];
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * At this point, the page may be truncated or
|
|
|
|
+ * invalidated (changing page->mapping to NULL), or
|
|
|
|
+ * even swizzled back from swapper_space to tmpfs file
|
|
|
|
+ * mapping. However, page->index will not change
|
|
|
|
+ * because we have a reference on the page.
|
|
|
|
+ */
|
|
|
|
+ if (page->index > end) {
|
|
|
|
+ done = 1;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ lock_page(page);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Page truncated or invalidated. We can freely skip it
|
|
|
|
+ * then, even for data integrity operations: the page
|
|
|
|
+ * has disappeared concurrently, so there could be no
|
|
|
|
+ * real expectation of this data interity operation
|
|
|
|
+ * even if there is now a new, dirty page at the same
|
|
|
|
+ * pagecache address.
|
|
|
|
+ */
|
|
|
|
+ if (unlikely(page->mapping != mapping)) {
|
|
|
|
+continue_unlock:
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!PageDirty(page)) {
|
|
|
|
+ /* someone wrote it for us */
|
|
|
|
+ goto continue_unlock;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (PageWriteback(page)) {
|
|
|
|
+ if (wbc->sync_mode != WB_SYNC_NONE)
|
|
|
|
+ wait_on_page_writeback(page);
|
|
|
|
+ else
|
|
|
|
+ goto continue_unlock;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ BUG_ON(PageWriteback(page));
|
|
|
|
+ if (!clear_page_dirty_for_io(page))
|
|
|
|
+ goto continue_unlock;
|
|
|
|
+
|
|
|
|
+ ret = __mpage_da_writepage(page, wbc, mpd);
|
|
|
|
+ if (unlikely(ret)) {
|
|
|
|
+ if (ret == AOP_WRITEPAGE_ACTIVATE) {
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ ret = 0;
|
|
|
|
+ } else {
|
|
|
|
+ done = 1;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (nr_to_write > 0) {
|
|
|
|
+ nr_to_write--;
|
|
|
|
+ if (nr_to_write == 0 &&
|
|
|
|
+ wbc->sync_mode == WB_SYNC_NONE) {
|
|
|
|
+ /*
|
|
|
|
+ * We stop writing back only if we are
|
|
|
|
+ * not doing integrity sync. In case of
|
|
|
|
+ * integrity sync we have to keep going
|
|
|
|
+ * because someone may be concurrently
|
|
|
|
+ * dirtying pages, and we might have
|
|
|
|
+ * synced a lot of newly appeared dirty
|
|
|
|
+ * pages, but have not synced all of the
|
|
|
|
+ * old dirty pages.
|
|
|
|
+ */
|
|
|
|
+ done = 1;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ pagevec_release(&pvec);
|
|
|
|
+ cond_resched();
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
static int ext4_da_writepages(struct address_space *mapping,
|
|
static int ext4_da_writepages(struct address_space *mapping,
|
|
struct writeback_control *wbc)
|
|
struct writeback_control *wbc)
|
|
{
|
|
{
|
|
@@ -2836,7 +2913,6 @@ static int ext4_da_writepages(struct address_space *mapping,
|
|
handle_t *handle = NULL;
|
|
handle_t *handle = NULL;
|
|
struct mpage_da_data mpd;
|
|
struct mpage_da_data mpd;
|
|
struct inode *inode = mapping->host;
|
|
struct inode *inode = mapping->host;
|
|
- int no_nrwrite_index_update;
|
|
|
|
int pages_written = 0;
|
|
int pages_written = 0;
|
|
long pages_skipped;
|
|
long pages_skipped;
|
|
unsigned int max_pages;
|
|
unsigned int max_pages;
|
|
@@ -2916,12 +2992,6 @@ static int ext4_da_writepages(struct address_space *mapping,
|
|
mpd.wbc = wbc;
|
|
mpd.wbc = wbc;
|
|
mpd.inode = mapping->host;
|
|
mpd.inode = mapping->host;
|
|
|
|
|
|
- /*
|
|
|
|
- * we don't want write_cache_pages to update
|
|
|
|
- * nr_to_write and writeback_index
|
|
|
|
- */
|
|
|
|
- no_nrwrite_index_update = wbc->no_nrwrite_index_update;
|
|
|
|
- wbc->no_nrwrite_index_update = 1;
|
|
|
|
pages_skipped = wbc->pages_skipped;
|
|
pages_skipped = wbc->pages_skipped;
|
|
|
|
|
|
retry:
|
|
retry:
|
|
@@ -2941,7 +3011,7 @@ retry:
|
|
if (IS_ERR(handle)) {
|
|
if (IS_ERR(handle)) {
|
|
ret = PTR_ERR(handle);
|
|
ret = PTR_ERR(handle);
|
|
ext4_msg(inode->i_sb, KERN_CRIT, "%s: jbd2_start: "
|
|
ext4_msg(inode->i_sb, KERN_CRIT, "%s: jbd2_start: "
|
|
- "%ld pages, ino %lu; err %d\n", __func__,
|
|
|
|
|
|
+ "%ld pages, ino %lu; err %d", __func__,
|
|
wbc->nr_to_write, inode->i_ino, ret);
|
|
wbc->nr_to_write, inode->i_ino, ret);
|
|
goto out_writepages;
|
|
goto out_writepages;
|
|
}
|
|
}
|
|
@@ -2963,8 +3033,7 @@ retry:
|
|
mpd.io_done = 0;
|
|
mpd.io_done = 0;
|
|
mpd.pages_written = 0;
|
|
mpd.pages_written = 0;
|
|
mpd.retval = 0;
|
|
mpd.retval = 0;
|
|
- ret = write_cache_pages(mapping, wbc, __mpage_da_writepage,
|
|
|
|
- &mpd);
|
|
|
|
|
|
+ ret = write_cache_pages_da(mapping, wbc, &mpd);
|
|
/*
|
|
/*
|
|
* If we have a contiguous extent of pages and we
|
|
* If we have a contiguous extent of pages and we
|
|
* haven't done the I/O yet, map the blocks and submit
|
|
* haven't done the I/O yet, map the blocks and submit
|
|
@@ -3016,7 +3085,7 @@ retry:
|
|
if (pages_skipped != wbc->pages_skipped)
|
|
if (pages_skipped != wbc->pages_skipped)
|
|
ext4_msg(inode->i_sb, KERN_CRIT,
|
|
ext4_msg(inode->i_sb, KERN_CRIT,
|
|
"This should not happen leaving %s "
|
|
"This should not happen leaving %s "
|
|
- "with nr_to_write = %ld ret = %d\n",
|
|
|
|
|
|
+ "with nr_to_write = %ld ret = %d",
|
|
__func__, wbc->nr_to_write, ret);
|
|
__func__, wbc->nr_to_write, ret);
|
|
|
|
|
|
/* Update index */
|
|
/* Update index */
|
|
@@ -3030,8 +3099,6 @@ retry:
|
|
mapping->writeback_index = index;
|
|
mapping->writeback_index = index;
|
|
|
|
|
|
out_writepages:
|
|
out_writepages:
|
|
- if (!no_nrwrite_index_update)
|
|
|
|
- wbc->no_nrwrite_index_update = 0;
|
|
|
|
wbc->nr_to_write -= nr_to_writebump;
|
|
wbc->nr_to_write -= nr_to_writebump;
|
|
wbc->range_start = range_start;
|
|
wbc->range_start = range_start;
|
|
trace_ext4_da_writepages_result(inode, wbc, ret, pages_written);
|
|
trace_ext4_da_writepages_result(inode, wbc, ret, pages_written);
|
|
@@ -3076,7 +3143,7 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
|
|
loff_t pos, unsigned len, unsigned flags,
|
|
loff_t pos, unsigned len, unsigned flags,
|
|
struct page **pagep, void **fsdata)
|
|
struct page **pagep, void **fsdata)
|
|
{
|
|
{
|
|
- int ret, retries = 0, quota_retries = 0;
|
|
|
|
|
|
+ int ret, retries = 0;
|
|
struct page *page;
|
|
struct page *page;
|
|
pgoff_t index;
|
|
pgoff_t index;
|
|
unsigned from, to;
|
|
unsigned from, to;
|
|
@@ -3135,22 +3202,6 @@ retry:
|
|
|
|
|
|
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
|
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
|
goto retry;
|
|
goto retry;
|
|
-
|
|
|
|
- if ((ret == -EDQUOT) &&
|
|
|
|
- EXT4_I(inode)->i_reserved_meta_blocks &&
|
|
|
|
- (quota_retries++ < 3)) {
|
|
|
|
- /*
|
|
|
|
- * Since we often over-estimate the number of meta
|
|
|
|
- * data blocks required, we may sometimes get a
|
|
|
|
- * spurios out of quota error even though there would
|
|
|
|
- * be enough space once we write the data blocks and
|
|
|
|
- * find out how many meta data blocks were _really_
|
|
|
|
- * required. So try forcing the inode write to see if
|
|
|
|
- * that helps.
|
|
|
|
- */
|
|
|
|
- write_inode_now(inode, (quota_retries == 3));
|
|
|
|
- goto retry;
|
|
|
|
- }
|
|
|
|
out:
|
|
out:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -3546,46 +3597,18 @@ out:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * ext4_get_block used when preparing for a DIO write or buffer write.
|
|
|
|
+ * We allocate an uinitialized extent if blocks haven't been allocated.
|
|
|
|
+ * The extent will be converted to initialized after the IO is complete.
|
|
|
|
+ */
|
|
static int ext4_get_block_write(struct inode *inode, sector_t iblock,
|
|
static int ext4_get_block_write(struct inode *inode, sector_t iblock,
|
|
struct buffer_head *bh_result, int create)
|
|
struct buffer_head *bh_result, int create)
|
|
{
|
|
{
|
|
- handle_t *handle = ext4_journal_current_handle();
|
|
|
|
- int ret = 0;
|
|
|
|
- unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
|
|
|
|
- int dio_credits;
|
|
|
|
- int started = 0;
|
|
|
|
-
|
|
|
|
ext4_debug("ext4_get_block_write: inode %lu, create flag %d\n",
|
|
ext4_debug("ext4_get_block_write: inode %lu, create flag %d\n",
|
|
inode->i_ino, create);
|
|
inode->i_ino, create);
|
|
- /*
|
|
|
|
- * ext4_get_block in prepare for a DIO write or buffer write.
|
|
|
|
- * We allocate an uinitialized extent if blocks haven't been allocated.
|
|
|
|
- * The extent will be converted to initialized after IO complete.
|
|
|
|
- */
|
|
|
|
- create = EXT4_GET_BLOCKS_IO_CREATE_EXT;
|
|
|
|
-
|
|
|
|
- if (!handle) {
|
|
|
|
- if (max_blocks > DIO_MAX_BLOCKS)
|
|
|
|
- max_blocks = DIO_MAX_BLOCKS;
|
|
|
|
- dio_credits = ext4_chunk_trans_blocks(inode, max_blocks);
|
|
|
|
- handle = ext4_journal_start(inode, dio_credits);
|
|
|
|
- if (IS_ERR(handle)) {
|
|
|
|
- ret = PTR_ERR(handle);
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
- started = 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ret = ext4_get_blocks(handle, inode, iblock, max_blocks, bh_result,
|
|
|
|
- create);
|
|
|
|
- if (ret > 0) {
|
|
|
|
- bh_result->b_size = (ret << inode->i_blkbits);
|
|
|
|
- ret = 0;
|
|
|
|
- }
|
|
|
|
- if (started)
|
|
|
|
- ext4_journal_stop(handle);
|
|
|
|
-out:
|
|
|
|
- return ret;
|
|
|
|
|
|
+ return _ext4_get_block(inode, iblock, bh_result,
|
|
|
|
+ EXT4_GET_BLOCKS_IO_CREATE_EXT);
|
|
}
|
|
}
|
|
|
|
|
|
static void dump_completed_IO(struct inode * inode)
|
|
static void dump_completed_IO(struct inode * inode)
|
|
@@ -3973,7 +3996,7 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb,
|
|
struct file *file = iocb->ki_filp;
|
|
struct file *file = iocb->ki_filp;
|
|
struct inode *inode = file->f_mapping->host;
|
|
struct inode *inode = file->f_mapping->host;
|
|
|
|
|
|
- if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)
|
|
|
|
|
|
+ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
|
|
return ext4_ext_direct_IO(rw, iocb, iov, offset, nr_segs);
|
|
return ext4_ext_direct_IO(rw, iocb, iov, offset, nr_segs);
|
|
|
|
|
|
return ext4_ind_direct_IO(rw, iocb, iov, offset, nr_segs);
|
|
return ext4_ind_direct_IO(rw, iocb, iov, offset, nr_segs);
|
|
@@ -4302,10 +4325,9 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
|
|
|
|
|
|
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free,
|
|
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free,
|
|
count)) {
|
|
count)) {
|
|
- ext4_error(inode->i_sb, "inode #%lu: "
|
|
|
|
- "attempt to clear blocks %llu len %lu, invalid",
|
|
|
|
- inode->i_ino, (unsigned long long) block_to_free,
|
|
|
|
- count);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode, "attempt to clear invalid "
|
|
|
|
+ "blocks %llu len %lu",
|
|
|
|
+ (unsigned long long) block_to_free, count);
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4410,11 +4432,10 @@ static void ext4_free_data(handle_t *handle, struct inode *inode,
|
|
if ((EXT4_JOURNAL(inode) == NULL) || bh2jh(this_bh))
|
|
if ((EXT4_JOURNAL(inode) == NULL) || bh2jh(this_bh))
|
|
ext4_handle_dirty_metadata(handle, inode, this_bh);
|
|
ext4_handle_dirty_metadata(handle, inode, this_bh);
|
|
else
|
|
else
|
|
- ext4_error(inode->i_sb,
|
|
|
|
- "circular indirect block detected, "
|
|
|
|
- "inode=%lu, block=%llu",
|
|
|
|
- inode->i_ino,
|
|
|
|
- (unsigned long long) this_bh->b_blocknr);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode,
|
|
|
|
+ "circular indirect block detected at "
|
|
|
|
+ "block %llu",
|
|
|
|
+ (unsigned long long) this_bh->b_blocknr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4452,11 +4473,10 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
|
|
|
|
|
|
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
|
|
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
|
|
nr, 1)) {
|
|
nr, 1)) {
|
|
- ext4_error(inode->i_sb,
|
|
|
|
- "indirect mapped block in inode "
|
|
|
|
- "#%lu invalid (level %d, blk #%lu)",
|
|
|
|
- inode->i_ino, depth,
|
|
|
|
- (unsigned long) nr);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode,
|
|
|
|
+ "invalid indirect mapped "
|
|
|
|
+ "block %lu (level %d)",
|
|
|
|
+ (unsigned long) nr, depth);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4468,9 +4488,9 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
|
|
* (should be rare).
|
|
* (should be rare).
|
|
*/
|
|
*/
|
|
if (!bh) {
|
|
if (!bh) {
|
|
- ext4_error(inode->i_sb,
|
|
|
|
- "Read failure, inode=%lu, block=%llu",
|
|
|
|
- inode->i_ino, nr);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode,
|
|
|
|
+ "Read failure block=%llu",
|
|
|
|
+ (unsigned long long) nr);
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4612,12 +4632,12 @@ void ext4_truncate(struct inode *inode)
|
|
if (!ext4_can_truncate(inode))
|
|
if (!ext4_can_truncate(inode))
|
|
return;
|
|
return;
|
|
|
|
|
|
- EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL;
|
|
|
|
|
|
+ ext4_clear_inode_flag(inode, EXT4_INODE_EOFBLOCKS);
|
|
|
|
|
|
if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC))
|
|
if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC))
|
|
ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);
|
|
ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);
|
|
|
|
|
|
- if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) {
|
|
|
|
|
|
+ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
|
|
ext4_ext_truncate(inode);
|
|
ext4_ext_truncate(inode);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
@@ -4785,8 +4805,8 @@ static int __ext4_get_inode_loc(struct inode *inode,
|
|
|
|
|
|
bh = sb_getblk(sb, block);
|
|
bh = sb_getblk(sb, block);
|
|
if (!bh) {
|
|
if (!bh) {
|
|
- ext4_error(sb, "unable to read inode block - "
|
|
|
|
- "inode=%lu, block=%llu", inode->i_ino, block);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode, "unable to read inode block - "
|
|
|
|
+ "block %llu", block);
|
|
return -EIO;
|
|
return -EIO;
|
|
}
|
|
}
|
|
if (!buffer_uptodate(bh)) {
|
|
if (!buffer_uptodate(bh)) {
|
|
@@ -4884,8 +4904,8 @@ make_io:
|
|
submit_bh(READ_META, bh);
|
|
submit_bh(READ_META, bh);
|
|
wait_on_buffer(bh);
|
|
wait_on_buffer(bh);
|
|
if (!buffer_uptodate(bh)) {
|
|
if (!buffer_uptodate(bh)) {
|
|
- ext4_error(sb, "unable to read inode block - inode=%lu,"
|
|
|
|
- " block=%llu", inode->i_ino, block);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode, "unable to read inode "
|
|
|
|
+ "block %llu", block);
|
|
brelse(bh);
|
|
brelse(bh);
|
|
return -EIO;
|
|
return -EIO;
|
|
}
|
|
}
|
|
@@ -5096,8 +5116,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
|
|
ret = 0;
|
|
ret = 0;
|
|
if (ei->i_file_acl &&
|
|
if (ei->i_file_acl &&
|
|
!ext4_data_block_valid(EXT4_SB(sb), ei->i_file_acl, 1)) {
|
|
!ext4_data_block_valid(EXT4_SB(sb), ei->i_file_acl, 1)) {
|
|
- ext4_error(sb, "bad extended attribute block %llu inode #%lu",
|
|
|
|
- ei->i_file_acl, inode->i_ino);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode, "bad extended attribute block %llu",
|
|
|
|
+ ei->i_file_acl);
|
|
ret = -EIO;
|
|
ret = -EIO;
|
|
goto bad_inode;
|
|
goto bad_inode;
|
|
} else if (ei->i_flags & EXT4_EXTENTS_FL) {
|
|
} else if (ei->i_flags & EXT4_EXTENTS_FL) {
|
|
@@ -5142,8 +5162,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
|
|
new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
|
|
new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
|
|
} else {
|
|
} else {
|
|
ret = -EIO;
|
|
ret = -EIO;
|
|
- ext4_error(inode->i_sb, "bogus i_mode (%o) for inode=%lu",
|
|
|
|
- inode->i_mode, inode->i_ino);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode, "bogus i_mode (%o)", inode->i_mode);
|
|
goto bad_inode;
|
|
goto bad_inode;
|
|
}
|
|
}
|
|
brelse(iloc.bh);
|
|
brelse(iloc.bh);
|
|
@@ -5381,9 +5400,9 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|
if (wbc->sync_mode == WB_SYNC_ALL)
|
|
if (wbc->sync_mode == WB_SYNC_ALL)
|
|
sync_dirty_buffer(iloc.bh);
|
|
sync_dirty_buffer(iloc.bh);
|
|
if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) {
|
|
if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) {
|
|
- ext4_error(inode->i_sb, "IO error syncing inode, "
|
|
|
|
- "inode=%lu, block=%llu", inode->i_ino,
|
|
|
|
- (unsigned long long)iloc.bh->b_blocknr);
|
|
|
|
|
|
+ EXT4_ERROR_INODE(inode,
|
|
|
|
+ "IO error syncing inode (block=%llu)",
|
|
|
|
+ (unsigned long long) iloc.bh->b_blocknr);
|
|
err = -EIO;
|
|
err = -EIO;
|
|
}
|
|
}
|
|
brelse(iloc.bh);
|
|
brelse(iloc.bh);
|
|
@@ -5455,7 +5474,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
|
}
|
|
}
|
|
|
|
|
|
if (attr->ia_valid & ATTR_SIZE) {
|
|
if (attr->ia_valid & ATTR_SIZE) {
|
|
- if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) {
|
|
|
|
|
|
+ if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
|
|
|
|
|
|
if (attr->ia_size > sbi->s_bitmap_maxbytes) {
|
|
if (attr->ia_size > sbi->s_bitmap_maxbytes) {
|
|
@@ -5468,7 +5487,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
|
if (S_ISREG(inode->i_mode) &&
|
|
if (S_ISREG(inode->i_mode) &&
|
|
attr->ia_valid & ATTR_SIZE &&
|
|
attr->ia_valid & ATTR_SIZE &&
|
|
(attr->ia_size < inode->i_size ||
|
|
(attr->ia_size < inode->i_size ||
|
|
- (EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL))) {
|
|
|
|
|
|
+ (ext4_test_inode_flag(inode, EXT4_INODE_EOFBLOCKS)))) {
|
|
handle_t *handle;
|
|
handle_t *handle;
|
|
|
|
|
|
handle = ext4_journal_start(inode, 3);
|
|
handle = ext4_journal_start(inode, 3);
|
|
@@ -5500,7 +5519,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* ext4_truncate will clear the flag */
|
|
/* ext4_truncate will clear the flag */
|
|
- if ((EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL))
|
|
|
|
|
|
+ if ((ext4_test_inode_flag(inode, EXT4_INODE_EOFBLOCKS)))
|
|
ext4_truncate(inode);
|
|
ext4_truncate(inode);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -5576,7 +5595,7 @@ static int ext4_indirect_trans_blocks(struct inode *inode, int nrblocks,
|
|
|
|
|
|
static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
|
|
static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
|
|
{
|
|
{
|
|
- if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL))
|
|
|
|
|
|
+ if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
|
|
return ext4_indirect_trans_blocks(inode, nrblocks, chunk);
|
|
return ext4_indirect_trans_blocks(inode, nrblocks, chunk);
|
|
return ext4_ext_index_trans_blocks(inode, nrblocks, chunk);
|
|
return ext4_ext_index_trans_blocks(inode, nrblocks, chunk);
|
|
}
|
|
}
|
|
@@ -5911,9 +5930,9 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
|
|
*/
|
|
*/
|
|
|
|
|
|
if (val)
|
|
if (val)
|
|
- EXT4_I(inode)->i_flags |= EXT4_JOURNAL_DATA_FL;
|
|
|
|
|
|
+ ext4_set_inode_flag(inode, EXT4_INODE_JOURNAL_DATA);
|
|
else
|
|
else
|
|
- EXT4_I(inode)->i_flags &= ~EXT4_JOURNAL_DATA_FL;
|
|
|
|
|
|
+ ext4_clear_inode_flag(inode, EXT4_INODE_JOURNAL_DATA);
|
|
ext4_set_aops(inode);
|
|
ext4_set_aops(inode);
|
|
|
|
|
|
jbd2_journal_unlock_updates(journal);
|
|
jbd2_journal_unlock_updates(journal);
|