Skip to content

Commit

Permalink
ext4: delayed allocation ENOSPC handling
Browse files Browse the repository at this point in the history
This patch does block reservation for delayed
allocation, to avoid ENOSPC later at page flush time.

Blocks(data and metadata) are reserved at da_write_begin()
time, the freeblocks counter is updated by then, and the number of
reserved blocks is store in per inode counter.
        
At the writepage time, the unused reserved meta blocks are returned
back. At unlink/truncate time, reserved blocks are properly released.

Updated fix from  Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
to fix the oldallocator block reservation accounting with delalloc, added
lock to guard the counters and also fix the reservation for meta blocks.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Mingming Cao <cmm@us.ibm.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
  • Loading branch information
Mingming Cao authored and Theodore Ts'o committed Jul 14, 2008
1 parent e8ced39 commit d2a1763
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 50 deletions.
49 changes: 32 additions & 17 deletions fs/ext4/balloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1701,7 +1701,12 @@ ext4_fsblk_t ext4_old_new_blocks(handle_t *handle, struct inode *inode,
}

sbi = EXT4_SB(sb);
*count = ext4_has_free_blocks(sbi, *count);
if (!EXT4_I(inode)->i_delalloc_reserved_flag) {
/*
* With delalloc we already reserved the blocks
*/
*count = ext4_has_free_blocks(sbi, *count);
}
if (*count == 0) {
*errp = -ENOSPC;
return 0; /*return with ENOSPC error */
Expand Down Expand Up @@ -1902,7 +1907,8 @@ ext4_fsblk_t ext4_old_new_blocks(handle_t *handle, struct inode *inode,
le16_add_cpu(&gdp->bg_free_blocks_count, -num);
gdp->bg_checksum = ext4_group_desc_csum(sbi, group_no, gdp);
spin_unlock(sb_bgl_lock(sbi, group_no));
percpu_counter_sub(&sbi->s_freeblocks_counter, num);
if (!EXT4_I(inode)->i_delalloc_reserved_flag)
percpu_counter_sub(&sbi->s_freeblocks_counter, num);

if (sbi->s_log_groups_per_flex) {
ext4_group_t flex_group = ext4_flex_group(sbi, group_no);
Expand Down Expand Up @@ -1976,40 +1982,49 @@ static ext4_fsblk_t do_blk_alloc(handle_t *handle, struct inode *inode,
}

/*
* ext4_new_meta_block() -- allocate block for meta data (indexing) blocks
* ext4_new_meta_blocks() -- allocate block for meta data (indexing) blocks
*
* @handle: handle to this transaction
* @inode: file inode
* @goal: given target block(filesystem wide)
* @count: total number of blocks need
* @errp: error code
*
* Return allocated block number on success
* Return 1st allocated block numberon success, *count stores total account
* error stores in errp pointer
*/
ext4_fsblk_t ext4_new_meta_block(handle_t *handle, struct inode *inode,
ext4_fsblk_t goal, int *errp)
ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t goal, unsigned long *count, int *errp)
{
unsigned long count = 1;
return do_blk_alloc(handle, inode, 0, goal,
&count, errp, EXT4_META_BLOCK);
ext4_fsblk_t ret;
ret = do_blk_alloc(handle, inode, 0, goal,
count, errp, EXT4_META_BLOCK);
/*
* Account for the allocated meta blocks
*/
if (!(*errp)) {
spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
EXT4_I(inode)->i_allocated_meta_blocks += *count;
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
}
return ret;
}

/*
* ext4_new_meta_blocks() -- allocate block for meta data (indexing) blocks
* ext4_new_meta_block() -- allocate block for meta data (indexing) blocks
*
* @handle: handle to this transaction
* @inode: file inode
* @goal: given target block(filesystem wide)
* @count: total number of blocks need
* @errp: error code
*
* Return 1st allocated block numberon success, *count stores total account
* error stores in errp pointer
* Return allocated block number on success
*/
ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t goal, unsigned long *count, int *errp)
ext4_fsblk_t ext4_new_meta_block(handle_t *handle, struct inode *inode,
ext4_fsblk_t goal, int *errp)
{
return do_blk_alloc(handle, inode, 0, goal,
count, errp, EXT4_META_BLOCK);
unsigned long count = 1;
return ext4_new_meta_blocks(handle, inode, goal, &count, errp);
}

/*
Expand Down
3 changes: 2 additions & 1 deletion fs/ext4/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ static int ext4_readdir(struct file * filp,
struct buffer_head *bh = NULL;

map_bh.b_state = 0;
err = ext4_get_blocks_wrap(NULL, inode, blk, 1, &map_bh, 0, 0);
err = ext4_get_blocks_wrap(NULL, inode, blk, 1, &map_bh,
0, 0, 0);
if (err > 0) {
pgoff_t index = map_bh.b_blocknr >>
(PAGE_CACHE_SHIFT - inode->i_blkbits);
Expand Down
6 changes: 5 additions & 1 deletion fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
#define EXT4_MB_HINT_GOAL_ONLY 256
/* goal is meaningful */
#define EXT4_MB_HINT_TRY_GOAL 512
/* blocks already pre-reserved by delayed allocation */
#define EXT4_MB_DELALLOC_RESERVED 1024


struct ext4_allocation_request {
/* target inode for block we're allocating */
Expand Down Expand Up @@ -1041,6 +1044,7 @@ extern void ext4_mb_update_group_info(struct ext4_group_info *grp,


/* inode.c */
void ext4_da_release_space(struct inode *inode, int used, int to_free);
int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode,
struct buffer_head *bh, ext4_fsblk_t blocknr);
struct buffer_head *ext4_getblk(handle_t *, struct inode *,
Expand Down Expand Up @@ -1234,7 +1238,7 @@ extern long ext4_fallocate(struct inode *inode, int mode, loff_t offset,
extern int ext4_get_blocks_wrap(handle_t *handle, struct inode *inode,
sector_t block, unsigned long max_blocks,
struct buffer_head *bh, int create,
int extend_disksize);
int extend_disksize, int flag);
#endif /* __KERNEL__ */

#endif /* _EXT4_H */
1 change: 1 addition & 0 deletions fs/ext4/ext4_extents.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ static inline int ext4_ext_get_actual_len(struct ext4_extent *ext)
(le16_to_cpu(ext->ee_len) - EXT_INIT_MAX_LEN));
}

extern int ext4_ext_calc_metadata_amount(struct inode *inode, int blocks);
extern ext4_fsblk_t idx_pblock(struct ext4_extent_idx *);
extern void ext4_ext_store_pblock(struct ext4_extent *, ext4_fsblk_t);
extern int ext4_extent_tree_init(handle_t *, struct inode *);
Expand Down
7 changes: 7 additions & 0 deletions fs/ext4/ext4_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ struct ext4_inode_info {
/* mballoc */
struct list_head i_prealloc_list;
spinlock_t i_prealloc_lock;

/* allocation reservation info for delalloc */
unsigned long i_reserved_data_blocks;
unsigned long i_reserved_meta_blocks;
unsigned long i_allocated_meta_blocks;
unsigned short i_delalloc_reserved_flag;
spinlock_t i_block_reservation_lock;
};

#endif /* _EXT4_I */
32 changes: 31 additions & 1 deletion fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,36 @@ static int ext4_ext_space_root_idx(struct inode *inode)
return size;
}

/*
* Calculate the number of metadata blocks needed
* to allocate @blocks
* Worse case is one block per extent
*/
int ext4_ext_calc_metadata_amount(struct inode *inode, int blocks)
{
int lcap, icap, rcap, leafs, idxs, num;
int newextents = blocks;

rcap = ext4_ext_space_root_idx(inode);
lcap = ext4_ext_space_block(inode);
icap = ext4_ext_space_block_idx(inode);

/* number of new leaf blocks needed */
num = leafs = (newextents + lcap - 1) / lcap;

/*
* Worse case, we need separate index block(s)
* to link all new leaf blocks
*/
idxs = (leafs + icap - 1) / icap;
do {
num += idxs;
idxs = (idxs + icap - 1) / icap;
} while (idxs > rcap);

return num;
}

static int
ext4_ext_max_entries(struct inode *inode, int depth)
{
Expand Down Expand Up @@ -2910,7 +2940,7 @@ long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len)
}
ret = ext4_get_blocks_wrap(handle, inode, block,
max_blocks, &map_bh,
EXT4_CREATE_UNINITIALIZED_EXT, 0);
EXT4_CREATE_UNINITIALIZED_EXT, 0, 0);
if (ret <= 0) {
#ifdef EXT4FS_DEBUG
WARN_ON(ret <= 0);
Expand Down
Loading

0 comments on commit d2a1763

Please sign in to comment.