提交 ee265627 编写于 作者: L Linus Torvalds

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: Update documentation to remind users to update mke2fs.conf
  ext4: Fix small file fragmentation
  ext4: Initialize writeback_index to 0 when allocating a new inode
  ext4: make sure ext4_has_free_blocks returns 0 for ENOSPC
  ext4: journal credit fix for the delayed allocation's writepages() function
  ext4: Rework the ext4_da_writepages() function
  ext4: journal credits reservation fixes for DIO, fallocate
  ext4: journal credits reservation fixes for extent file writepage
  ext4: journal credits calulation cleanup and fix for non-extent writepage
  ext4: Fix bug where we return ENOSPC even though we have plenty of inodes
  ext4: don't try to resize if there are no reserved gdt blocks left
  ext4: Use ext4_discard_reservations instead of mballoc-specific call
  ext4: Fix ext4_dx_readdir hash collision handling
  ext4: Fix delalloc release block reservation for truncate
  ext4: Fix potential truncate BUG due to i_prealloc_list being non-empty
  ext4: Handle unwritten extent properly with delayed allocation
...@@ -26,6 +26,12 @@ Mailing list: linux-ext4@vger.kernel.org ...@@ -26,6 +26,12 @@ Mailing list: linux-ext4@vger.kernel.org
git://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git git://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
- Note that it is highly important to install the mke2fs.conf file
that comes with the e2fsprogs 1.41.x sources in /etc/mke2fs.conf. If
you have edited the /etc/mke2fs.conf file installed on your system,
you will need to merge your changes with the version from e2fsprogs
1.41.x.
- Create a new filesystem using the ext4dev filesystem type: - Create a new filesystem using the ext4dev filesystem type:
# mke2fs -t ext4dev /dev/hda1 # mke2fs -t ext4dev /dev/hda1
......
...@@ -1626,6 +1626,9 @@ ext4_fsblk_t ext4_has_free_blocks(struct ext4_sb_info *sbi, ...@@ -1626,6 +1626,9 @@ ext4_fsblk_t ext4_has_free_blocks(struct ext4_sb_info *sbi,
free_blocks = free_blocks =
percpu_counter_sum_and_set(&sbi->s_freeblocks_counter); percpu_counter_sum_and_set(&sbi->s_freeblocks_counter);
#endif #endif
if (free_blocks <= root_blocks)
/* we don't have free space */
return 0;
if (free_blocks - root_blocks < nblocks) if (free_blocks - root_blocks < nblocks)
return free_blocks - root_blocks; return free_blocks - root_blocks;
return nblocks; return nblocks;
......
...@@ -411,7 +411,7 @@ static int call_filldir(struct file * filp, void * dirent, ...@@ -411,7 +411,7 @@ static int call_filldir(struct file * filp, void * dirent,
get_dtype(sb, fname->file_type)); get_dtype(sb, fname->file_type));
if (error) { if (error) {
filp->f_pos = curr_pos; filp->f_pos = curr_pos;
info->extra_fname = fname->next; info->extra_fname = fname;
return error; return error;
} }
fname = fname->next; fname = fname->next;
...@@ -450,11 +450,21 @@ static int ext4_dx_readdir(struct file * filp, ...@@ -450,11 +450,21 @@ static int ext4_dx_readdir(struct file * filp,
* If there are any leftover names on the hash collision * If there are any leftover names on the hash collision
* chain, return them first. * chain, return them first.
*/ */
if (info->extra_fname && if (info->extra_fname) {
call_filldir(filp, dirent, filldir, info->extra_fname)) if (call_filldir(filp, dirent, filldir, info->extra_fname))
goto finished; goto finished;
if (!info->curr_node) info->extra_fname = NULL;
info->curr_node = rb_next(info->curr_node);
if (!info->curr_node) {
if (info->next_hash == ~0) {
filp->f_pos = EXT4_HTREE_EOF;
goto finished;
}
info->curr_hash = info->next_hash;
info->curr_minor_hash = 0;
}
} else if (!info->curr_node)
info->curr_node = rb_first(&info->root); info->curr_node = rb_first(&info->root);
while (1) { while (1) {
......
...@@ -1072,6 +1072,8 @@ extern void ext4_set_inode_flags(struct inode *); ...@@ -1072,6 +1072,8 @@ extern void ext4_set_inode_flags(struct inode *);
extern void ext4_get_inode_flags(struct ext4_inode_info *); extern void ext4_get_inode_flags(struct ext4_inode_info *);
extern void ext4_set_aops(struct inode *inode); extern void ext4_set_aops(struct inode *inode);
extern int ext4_writepage_trans_blocks(struct inode *); extern int ext4_writepage_trans_blocks(struct inode *);
extern int ext4_meta_trans_blocks(struct inode *, int nrblocks, int idxblocks);
extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks);
extern int ext4_block_truncate_page(handle_t *handle, extern int ext4_block_truncate_page(handle_t *handle,
struct address_space *mapping, loff_t from); struct address_space *mapping, loff_t from);
extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page); extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page);
...@@ -1227,6 +1229,8 @@ extern const struct inode_operations ext4_fast_symlink_inode_operations; ...@@ -1227,6 +1229,8 @@ extern const struct inode_operations ext4_fast_symlink_inode_operations;
/* extents.c */ /* extents.c */
extern int ext4_ext_tree_init(handle_t *handle, struct inode *); extern int ext4_ext_tree_init(handle_t *handle, struct inode *);
extern int ext4_ext_writepage_trans_blocks(struct inode *, int); extern int ext4_ext_writepage_trans_blocks(struct inode *, int);
extern int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks,
int chunk);
extern int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, extern int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
ext4_lblk_t iblock, ext4_lblk_t iblock,
unsigned long max_blocks, struct buffer_head *bh_result, unsigned long max_blocks, struct buffer_head *bh_result,
......
...@@ -216,7 +216,9 @@ extern int ext4_ext_calc_metadata_amount(struct inode *inode, int blocks); ...@@ -216,7 +216,9 @@ extern int ext4_ext_calc_metadata_amount(struct inode *inode, int blocks);
extern ext4_fsblk_t idx_pblock(struct ext4_extent_idx *); extern ext4_fsblk_t idx_pblock(struct ext4_extent_idx *);
extern void ext4_ext_store_pblock(struct ext4_extent *, ext4_fsblk_t); extern void ext4_ext_store_pblock(struct ext4_extent *, ext4_fsblk_t);
extern int ext4_extent_tree_init(handle_t *, struct inode *); extern int ext4_extent_tree_init(handle_t *, struct inode *);
extern int ext4_ext_calc_credits_for_insert(struct inode *, struct ext4_ext_path *); extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode,
int num,
struct ext4_ext_path *path);
extern int ext4_ext_try_to_merge(struct inode *inode, extern int ext4_ext_try_to_merge(struct inode *inode,
struct ext4_ext_path *path, struct ext4_ext_path *path,
struct ext4_extent *); struct ext4_extent *);
......
...@@ -51,6 +51,14 @@ ...@@ -51,6 +51,14 @@
EXT4_XATTR_TRANS_BLOCKS - 2 + \ EXT4_XATTR_TRANS_BLOCKS - 2 + \
2*EXT4_QUOTA_TRANS_BLOCKS(sb)) 2*EXT4_QUOTA_TRANS_BLOCKS(sb))
/*
* Define the number of metadata blocks we need to account to modify data.
*
* This include super block, inode block, quota blocks and xattr blocks
*/
#define EXT4_META_TRANS_BLOCKS(sb) (EXT4_XATTR_TRANS_BLOCKS + \
2*EXT4_QUOTA_TRANS_BLOCKS(sb))
/* Delete operations potentially hit one directory's namespace plus an /* Delete operations potentially hit one directory's namespace plus an
* entire inode, plus arbitrary amounts of bitmap/indirection data. Be * entire inode, plus arbitrary amounts of bitmap/indirection data. Be
* generous. We can grow the delete transaction later if necessary. */ * generous. We can grow the delete transaction later if necessary. */
......
...@@ -1747,54 +1747,61 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode, ...@@ -1747,54 +1747,61 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
} }
/* /*
* ext4_ext_calc_credits_for_insert: * ext4_ext_calc_credits_for_single_extent:
* This routine returns max. credits that the extent tree can consume. * This routine returns max. credits that needed to insert an extent
* It should be OK for low-performance paths like ->writepage() * to the extent tree.
* To allow many writing processes to fit into a single transaction, * When pass the actual path, the caller should calculate credits
* the caller should calculate credits under i_data_sem and * under i_data_sem.
* pass the actual path.
*/ */
int ext4_ext_calc_credits_for_insert(struct inode *inode, int ext4_ext_calc_credits_for_single_extent(struct inode *inode, int nrblocks,
struct ext4_ext_path *path) struct ext4_ext_path *path)
{ {
int depth, needed;
if (path) { if (path) {
int depth = ext_depth(inode);
int ret = 0;
/* probably there is space in leaf? */ /* probably there is space in leaf? */
depth = ext_depth(inode);
if (le16_to_cpu(path[depth].p_hdr->eh_entries) if (le16_to_cpu(path[depth].p_hdr->eh_entries)
< le16_to_cpu(path[depth].p_hdr->eh_max)) < le16_to_cpu(path[depth].p_hdr->eh_max)) {
return 1;
}
/* /*
* given 32-bit logical block (4294967296 blocks), max. tree * There are some space in the leaf tree, no
* can be 4 levels in depth -- 4 * 340^4 == 53453440000. * need to account for leaf block credit
* Let's also add one more level for imbalance. *
*/ * bitmaps and block group descriptor blocks
depth = 5; * and other metadat blocks still need to be
* accounted.
/* allocation of new data block(s) */ */
needed = 2; /* 1 bitmap, 1 block group descriptor */
ret = 2 + EXT4_META_TRANS_BLOCKS(inode->i_sb);
}
}
/* return ext4_chunk_trans_blocks(inode, nrblocks);
* tree can be full, so it would need to grow in depth: }
* we need one credit to modify old root, credits for
* new root will be added in split accounting
*/
needed += 1;
/* /*
* Index split can happen, we would need: * How many index/leaf blocks need to change/allocate to modify nrblocks?
* allocate intermediate indexes (bitmap + group) *
* + change two blocks at each level, but root (already included) * if nrblocks are fit in a single extent (chunk flag is 1), then
*/ * in the worse case, each tree level index/leaf need to be changed
needed += (depth * 2) + (depth * 2); * if the tree split due to insert a new extent, then the old tree
* index/leaf need to be updated too
*
* If the nrblocks are discontiguous, they could cause
* the whole tree split more than once, but this is really rare.
*/
int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
{
int index;
int depth = ext_depth(inode);
/* any allocation modifies superblock */ if (chunk)
needed += 1; index = depth * 2;
else
index = depth * 3;
return needed; return index;
} }
static int ext4_remove_blocks(handle_t *handle, struct inode *inode, static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
...@@ -1921,9 +1928,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ...@@ -1921,9 +1928,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
correct_index = 1; correct_index = 1;
credits += (ext_depth(inode)) + 1; credits += (ext_depth(inode)) + 1;
} }
#ifdef CONFIG_QUOTA
credits += 2 * EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb); credits += 2 * EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb);
#endif
err = ext4_ext_journal_restart(handle, credits); err = ext4_ext_journal_restart(handle, credits);
if (err) if (err)
...@@ -2805,7 +2810,7 @@ void ext4_ext_truncate(struct inode *inode) ...@@ -2805,7 +2810,7 @@ void ext4_ext_truncate(struct inode *inode)
/* /*
* probably first extent we're gonna free will be last in block * probably first extent we're gonna free will be last in block
*/ */
err = ext4_writepage_trans_blocks(inode) + 3; err = ext4_writepage_trans_blocks(inode);
handle = ext4_journal_start(inode, err); handle = ext4_journal_start(inode, err);
if (IS_ERR(handle)) if (IS_ERR(handle))
return; return;
...@@ -2819,7 +2824,7 @@ void ext4_ext_truncate(struct inode *inode) ...@@ -2819,7 +2824,7 @@ void ext4_ext_truncate(struct inode *inode)
down_write(&EXT4_I(inode)->i_data_sem); down_write(&EXT4_I(inode)->i_data_sem);
ext4_ext_invalidate_cache(inode); ext4_ext_invalidate_cache(inode);
ext4_mb_discard_inode_preallocations(inode); ext4_discard_reservation(inode);
/* /*
* TODO: optimization is possible here. * TODO: optimization is possible here.
...@@ -2858,27 +2863,6 @@ void ext4_ext_truncate(struct inode *inode) ...@@ -2858,27 +2863,6 @@ void ext4_ext_truncate(struct inode *inode)
ext4_journal_stop(handle); ext4_journal_stop(handle);
} }
/*
* ext4_ext_writepage_trans_blocks:
* calculate max number of blocks we could modify
* in order to allocate new block for an inode
*/
int ext4_ext_writepage_trans_blocks(struct inode *inode, int num)
{
int needed;
needed = ext4_ext_calc_credits_for_insert(inode, NULL);
/* caller wants to allocate num blocks, but note it includes sb */
needed = needed * num - (num - 1);
#ifdef CONFIG_QUOTA
needed += 2 * EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb);
#endif
return needed;
}
static void ext4_falloc_update_inode(struct inode *inode, static void ext4_falloc_update_inode(struct inode *inode,
int mode, loff_t new_size, int update_ctime) int mode, loff_t new_size, int update_ctime)
{ {
...@@ -2939,10 +2923,9 @@ long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len) ...@@ -2939,10 +2923,9 @@ long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len)
max_blocks = (EXT4_BLOCK_ALIGN(len + offset, blkbits) >> blkbits) max_blocks = (EXT4_BLOCK_ALIGN(len + offset, blkbits) >> blkbits)
- block; - block;
/* /*
* credits to insert 1 extent into extent tree + buffers to be able to * credits to insert 1 extent into extent tree
* modify 1 super block, 1 block bitmap and 1 group descriptor.
*/ */
credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + 3; credits = ext4_chunk_trans_blocks(inode, max_blocks);
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
retry: retry:
while (ret >= 0 && ret < max_blocks) { while (ret >= 0 && ret < max_blocks) {
......
...@@ -351,7 +351,7 @@ static int find_group_flex(struct super_block *sb, struct inode *parent, ...@@ -351,7 +351,7 @@ static int find_group_flex(struct super_block *sb, struct inode *parent,
goto found_flexbg; goto found_flexbg;
} }
if (best_flex < 0 || if (flex_group[best_flex].free_inodes == 0 ||
(flex_group[i].free_blocks > (flex_group[i].free_blocks >
flex_group[best_flex].free_blocks && flex_group[best_flex].free_blocks &&
flex_group[i].free_inodes)) flex_group[i].free_inodes))
......
此差异已折叠。
...@@ -3281,6 +3281,35 @@ static void ext4_mb_use_group_pa(struct ext4_allocation_context *ac, ...@@ -3281,6 +3281,35 @@ static void ext4_mb_use_group_pa(struct ext4_allocation_context *ac,
mb_debug("use %u/%u from group pa %p\n", pa->pa_lstart-len, len, pa); mb_debug("use %u/%u from group pa %p\n", pa->pa_lstart-len, len, pa);
} }
/*
* Return the prealloc space that have minimal distance
* from the goal block. @cpa is the prealloc
* space that is having currently known minimal distance
* from the goal block.
*/
static struct ext4_prealloc_space *
ext4_mb_check_group_pa(ext4_fsblk_t goal_block,
struct ext4_prealloc_space *pa,
struct ext4_prealloc_space *cpa)
{
ext4_fsblk_t cur_distance, new_distance;
if (cpa == NULL) {
atomic_inc(&pa->pa_count);
return pa;
}
cur_distance = abs(goal_block - cpa->pa_pstart);
new_distance = abs(goal_block - pa->pa_pstart);
if (cur_distance < new_distance)
return cpa;
/* drop the previous reference */
atomic_dec(&cpa->pa_count);
atomic_inc(&pa->pa_count);
return pa;
}
/* /*
* search goal blocks in preallocated space * search goal blocks in preallocated space
*/ */
...@@ -3290,7 +3319,8 @@ ext4_mb_use_preallocated(struct ext4_allocation_context *ac) ...@@ -3290,7 +3319,8 @@ ext4_mb_use_preallocated(struct ext4_allocation_context *ac)
int order, i; int order, i;
struct ext4_inode_info *ei = EXT4_I(ac->ac_inode); struct ext4_inode_info *ei = EXT4_I(ac->ac_inode);
struct ext4_locality_group *lg; struct ext4_locality_group *lg;
struct ext4_prealloc_space *pa; struct ext4_prealloc_space *pa, *cpa = NULL;
ext4_fsblk_t goal_block;
/* only data can be preallocated */ /* only data can be preallocated */
if (!(ac->ac_flags & EXT4_MB_HINT_DATA)) if (!(ac->ac_flags & EXT4_MB_HINT_DATA))
...@@ -3333,6 +3363,13 @@ ext4_mb_use_preallocated(struct ext4_allocation_context *ac) ...@@ -3333,6 +3363,13 @@ ext4_mb_use_preallocated(struct ext4_allocation_context *ac)
/* The max size of hash table is PREALLOC_TB_SIZE */ /* The max size of hash table is PREALLOC_TB_SIZE */
order = PREALLOC_TB_SIZE - 1; order = PREALLOC_TB_SIZE - 1;
goal_block = ac->ac_g_ex.fe_group * EXT4_BLOCKS_PER_GROUP(ac->ac_sb) +
ac->ac_g_ex.fe_start +
le32_to_cpu(EXT4_SB(ac->ac_sb)->s_es->s_first_data_block);
/*
* search for the prealloc space that is having
* minimal distance from the goal block.
*/
for (i = order; i < PREALLOC_TB_SIZE; i++) { for (i = order; i < PREALLOC_TB_SIZE; i++) {
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(pa, &lg->lg_prealloc_list[i], list_for_each_entry_rcu(pa, &lg->lg_prealloc_list[i],
...@@ -3340,17 +3377,19 @@ ext4_mb_use_preallocated(struct ext4_allocation_context *ac) ...@@ -3340,17 +3377,19 @@ ext4_mb_use_preallocated(struct ext4_allocation_context *ac)
spin_lock(&pa->pa_lock); spin_lock(&pa->pa_lock);
if (pa->pa_deleted == 0 && if (pa->pa_deleted == 0 &&
pa->pa_free >= ac->ac_o_ex.fe_len) { pa->pa_free >= ac->ac_o_ex.fe_len) {
atomic_inc(&pa->pa_count);
ext4_mb_use_group_pa(ac, pa); cpa = ext4_mb_check_group_pa(goal_block,
spin_unlock(&pa->pa_lock); pa, cpa);
ac->ac_criteria = 20;
rcu_read_unlock();
return 1;
} }
spin_unlock(&pa->pa_lock); spin_unlock(&pa->pa_lock);
} }
rcu_read_unlock(); rcu_read_unlock();
} }
if (cpa) {
ext4_mb_use_group_pa(ac, cpa);
ac->ac_criteria = 20;
return 1;
}
return 0; return 0;
} }
......
...@@ -53,7 +53,8 @@ static int finish_range(handle_t *handle, struct inode *inode, ...@@ -53,7 +53,8 @@ static int finish_range(handle_t *handle, struct inode *inode,
* credit. But below we try to not accumalate too much * credit. But below we try to not accumalate too much
* of them by restarting the journal. * of them by restarting the journal.
*/ */
needed = ext4_ext_calc_credits_for_insert(inode, path); needed = ext4_ext_calc_credits_for_single_extent(inode,
lb->last_block - lb->first_block + 1, path);
/* /*
* Make sure the credit we accumalated is not really high * Make sure the credit we accumalated is not really high
......
...@@ -773,7 +773,8 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) ...@@ -773,7 +773,8 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
if (reserved_gdb || gdb_off == 0) { if (reserved_gdb || gdb_off == 0) {
if (!EXT4_HAS_COMPAT_FEATURE(sb, if (!EXT4_HAS_COMPAT_FEATURE(sb,
EXT4_FEATURE_COMPAT_RESIZE_INODE)){ EXT4_FEATURE_COMPAT_RESIZE_INODE)
|| !le16_to_cpu(es->s_reserved_gdt_blocks)) {
ext4_warning(sb, __func__, ext4_warning(sb, __func__,
"No reserved GDT blocks, can't resize"); "No reserved GDT blocks, can't resize");
return -EPERM; return -EPERM;
......
...@@ -568,6 +568,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) ...@@ -568,6 +568,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
#endif #endif
ei->i_block_alloc_info = NULL; ei->i_block_alloc_info = NULL;
ei->vfs_inode.i_version = 1; ei->vfs_inode.i_version = 1;
ei->vfs_inode.i_data.writeback_index = 0;
memset(&ei->i_cached_extent, 0, sizeof(struct ext4_ext_cache)); memset(&ei->i_cached_extent, 0, sizeof(struct ext4_ext_cache));
INIT_LIST_HEAD(&ei->i_prealloc_list); INIT_LIST_HEAD(&ei->i_prealloc_list);
spin_lock_init(&ei->i_prealloc_lock); spin_lock_init(&ei->i_prealloc_lock);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册