From: Mingming Cao Right now the ext3 reservation structure(ext3_reserve_window_node) is part of the ext3 inode itself. This part of information is only needed for files that need allocate blocks on disk. So, the attached patches reduce the ext3 inode size by dynamically allocating the block allocation/reservation info structure(called struct ext3_block_alloc_info) when it is needed(i.e. only for files who need to allocate blocks) The reservation structure is being allocated and linked to the ext3 inode at ext3_get_block_handle(), and being freed and unlinked at the iput_final->ext3_clear_inode(). The ei->truncate_sem which is currently used to protect concurrent ext3_get_block() and ext3_truncate is used to protect reservation structure allocation and deallocation. Signed-off-by: Mingming Cao Signed-off-by: Andrew Morton --- 25-akpm/fs/ext3/balloc.c | 34 ++++++++++++++++++++++++++++------ 25-akpm/fs/ext3/ialloc.c | 6 +----- 25-akpm/fs/ext3/inode.c | 19 ++++++++++++------- 25-akpm/fs/ext3/ioctl.c | 24 ++++++++++++++++++++---- 25-akpm/fs/ext3/super.c | 5 ++++- 25-akpm/include/linux/ext3_fs.h | 1 + 25-akpm/include/linux/ext3_fs_i.h | 4 ++-- 7 files changed, 68 insertions(+), 25 deletions(-) diff -puN fs/ext3/balloc.c~ext3-dynamic-allocating-block-reservation-info fs/ext3/balloc.c --- 25/fs/ext3/balloc.c~ext3-dynamic-allocating-block-reservation-info 2005-03-23 01:33:48.000000000 -0800 +++ 25-akpm/fs/ext3/balloc.c 2005-03-23 01:34:02.000000000 -0800 @@ -259,14 +259,29 @@ static inline int rsv_is_empty(struct ex /* a valid reservation end block could not be 0 */ return (rsv->_rsv_end == EXT3_RESERVE_WINDOW_NOT_ALLOCATED); } +void ext3_alloc_init_reservation(struct inode *inode) +{ + struct ext3_inode_info *ei = EXT3_I(inode); + struct ext3_reserve_window_node *rsv = ei->i_rsv_window; + + rsv = kmalloc(sizeof(*rsv), GFP_NOFS); + if (rsv) { + rsv->rsv_start = EXT3_RESERVE_WINDOW_NOT_ALLOCATED; + rsv->rsv_end = EXT3_RESERVE_WINDOW_NOT_ALLOCATED; + atomic_set(&rsv->rsv_goal_size, EXT3_DEFAULT_RESERVE_BLOCKS); + atomic_set(&rsv->rsv_alloc_hit, 0); + seqlock_init(&rsv->rsv_seqlock); + } + ei->i_rsv_window = rsv; +} void ext3_discard_reservation(struct inode *inode) { struct ext3_inode_info *ei = EXT3_I(inode); - struct ext3_reserve_window_node *rsv = &ei->i_rsv_window; + struct ext3_reserve_window_node *rsv = ei->i_rsv_window; spinlock_t *rsv_lock = &EXT3_SB(inode->i_sb)->s_rsv_window_lock; - if (!rsv_is_empty(&rsv->rsv_window)) { + if (rsv && !rsv_is_empty(&rsv->rsv_window)) { spin_lock(rsv_lock); if (!rsv_is_empty(&rsv->rsv_window)) rsv_window_remove(inode->i_sb, rsv); @@ -1154,7 +1169,7 @@ int ext3_new_block(handle_t *handle, str struct ext3_super_block *es; struct ext3_sb_info *sbi; struct ext3_reserve_window_node *my_rsv = NULL; - struct ext3_reserve_window_node *rsv = &EXT3_I(inode)->i_rsv_window; + struct ext3_reserve_window_node *rsv = EXT3_I(inode)->i_rsv_window; unsigned short windowsz = 0; #ifdef EXT3FS_DEBUG static int goal_hits, goal_attempts; @@ -1187,10 +1202,9 @@ int ext3_new_block(handle_t *handle, str * command EXT3_IOC_SETRSVSZ to set the window size to 0 to turn off * reservation on that particular file) */ - windowsz = atomic_read(&rsv->rsv_goal_size); - if (test_opt(sb, RESERVATION) && - S_ISREG(inode->i_mode) && (windowsz > 0)) + if (rsv && ((windowsz = atomic_read(&rsv->rsv_goal_size)) > 0)) my_rsv = rsv; + if (!ext3_has_free_blocks(sbi)) { *errp = -ENOSPC; goto out; @@ -1211,6 +1225,14 @@ int ext3_new_block(handle_t *handle, str goal_group = group_no; retry: free_blocks = le16_to_cpu(gdp->bg_free_blocks_count); + /* + * if there is not enough free blocks to make a new resevation + * turn off reservation for this allocation + */ + if (my_rsv && (free_blocks < windowsz) + && (rsv_is_empty(&my_rsv->rsv_window))) + my_rsv = NULL; + if (free_blocks > 0) { ret_block = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT3_BLOCKS_PER_GROUP(sb)); diff -puN fs/ext3/ialloc.c~ext3-dynamic-allocating-block-reservation-info fs/ext3/ialloc.c --- 25/fs/ext3/ialloc.c~ext3-dynamic-allocating-block-reservation-info 2005-03-23 01:33:48.000000000 -0800 +++ 25-akpm/fs/ext3/ialloc.c 2005-03-23 01:34:02.000000000 -0800 @@ -581,11 +581,7 @@ got: ei->i_file_acl = 0; ei->i_dir_acl = 0; ei->i_dtime = 0; - ei->i_rsv_window.rsv_start = EXT3_RESERVE_WINDOW_NOT_ALLOCATED; - ei->i_rsv_window.rsv_end = EXT3_RESERVE_WINDOW_NOT_ALLOCATED; - atomic_set(&ei->i_rsv_window.rsv_goal_size, EXT3_DEFAULT_RESERVE_BLOCKS); - atomic_set(&ei->i_rsv_window.rsv_alloc_hit, 0); - seqlock_init(&ei->i_rsv_window.rsv_seqlock); + ei->i_rsv_window = NULL; ei->i_block_group = group; ext3_set_inode_flags(inode); diff -puN fs/ext3/inode.c~ext3-dynamic-allocating-block-reservation-info fs/ext3/inode.c --- 25/fs/ext3/inode.c~ext3-dynamic-allocating-block-reservation-info 2005-03-23 01:33:48.000000000 -0800 +++ 25-akpm/fs/ext3/inode.c 2005-03-23 01:34:02.000000000 -0800 @@ -708,6 +708,7 @@ ext3_get_block_handle(handle_t *handle, int boundary = 0; int depth = ext3_block_to_path(inode, iblock, offsets, &boundary); struct ext3_inode_info *ei = EXT3_I(inode); + struct super_block *sb = inode->i_sb; J_ASSERT(handle != NULL || create == 0); @@ -752,6 +753,13 @@ out: goal = 0; down(&ei->truncate_sem); + + /* lazy initialize the block allocation info here if necessary */ + if (test_opt(sb, RESERVATION) && S_ISREG(inode->i_mode) + && (!ei->i_rsv_window)) { + ext3_alloc_init_reservation(inode); + } + if (ext3_find_goal(inode, iblock, chain, partial, &goal) < 0) { up(&ei->truncate_sem); goto changed; @@ -2149,8 +2157,6 @@ void ext3_truncate(struct inode * inode) if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; - ext3_discard_reservation(inode); - /* * We have to lock the EOF page here, because lock_page() nests * outside journal_start(). @@ -2275,6 +2281,9 @@ do_indirects: case EXT3_TIND_BLOCK: ; } + + ext3_discard_reservation(inode); + up(&ei->truncate_sem); inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; ext3_mark_inode_dirty(handle, inode); @@ -2494,7 +2503,7 @@ void ext3_read_inode(struct inode * inod ei->i_acl = EXT3_ACL_NOT_CACHED; ei->i_default_acl = EXT3_ACL_NOT_CACHED; #endif - ei->i_rsv_window.rsv_end = EXT3_RESERVE_WINDOW_NOT_ALLOCATED; + ei->i_rsv_window = NULL; if (__ext3_get_inode_loc(inode, &iloc, 0)) goto bad_inode; @@ -2556,10 +2565,6 @@ void ext3_read_inode(struct inode * inod ei->i_disksize = inode->i_size; inode->i_generation = le32_to_cpu(raw_inode->i_generation); ei->i_block_group = iloc.block_group; - ei->i_rsv_window.rsv_start = 0; - ei->i_rsv_window.rsv_end= 0; - atomic_set(&ei->i_rsv_window.rsv_goal_size, EXT3_DEFAULT_RESERVE_BLOCKS); - seqlock_init(&ei->i_rsv_window.rsv_seqlock); /* * NOTE! The in-memory inode i_data array is in little-endian order * even on big-endian machines: we do NOT byteswap the block numbers! diff -puN fs/ext3/ioctl.c~ext3-dynamic-allocating-block-reservation-info fs/ext3/ioctl.c --- 25/fs/ext3/ioctl.c~ext3-dynamic-allocating-block-reservation-info 2005-03-23 01:33:48.000000000 -0800 +++ 25-akpm/fs/ext3/ioctl.c 2005-03-23 01:34:02.000000000 -0800 @@ -153,12 +153,15 @@ flags_err: } #endif case EXT3_IOC_GETRSVSZ: - if (test_opt(inode->i_sb, RESERVATION) && S_ISREG(inode->i_mode)) { - rsv_window_size = atomic_read(&ei->i_rsv_window.rsv_goal_size); + if (test_opt(inode->i_sb, RESERVATION) + && S_ISREG(inode->i_mode) + && ei->i_rsv_window) { + rsv_window_size = atomic_read(&ei->i_rsv_window->rsv_goal_size); return put_user(rsv_window_size, (int __user *)arg); } return -ENOTTY; - case EXT3_IOC_SETRSVSZ: + case EXT3_IOC_SETRSVSZ: { + if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) return -ENOTTY; @@ -173,8 +176,21 @@ flags_err: if (rsv_window_size > EXT3_MAX_RESERVE_BLOCKS) rsv_window_size = EXT3_MAX_RESERVE_BLOCKS; - atomic_set(&ei->i_rsv_window.rsv_goal_size, rsv_window_size); + + /* + * need to allocate reservation structure for this inode + * before set the window size + */ + down(&ei->truncate_sem); + if (!ei->i_rsv_window) + ext3_alloc_init_reservation(inode); + + if (ei->i_rsv_window) + atomic_set(&ei->i_rsv_window->rsv_goal_size, + rsv_window_size); + up(&ei->truncate_sem); return 0; + } case EXT3_IOC_GROUP_EXTEND: { unsigned long n_blocks_count; struct super_block *sb = inode->i_sb; diff -puN fs/ext3/super.c~ext3-dynamic-allocating-block-reservation-info fs/ext3/super.c --- 25/fs/ext3/super.c~ext3-dynamic-allocating-block-reservation-info 2005-03-23 01:33:48.000000000 -0800 +++ 25-akpm/fs/ext3/super.c 2005-03-23 01:34:02.000000000 -0800 @@ -441,7 +441,7 @@ static struct inode *ext3_alloc_inode(st ei->i_acl = EXT3_ACL_NOT_CACHED; ei->i_default_acl = EXT3_ACL_NOT_CACHED; #endif - ei->i_rsv_window.rsv_end = EXT3_RESERVE_WINDOW_NOT_ALLOCATED; + ei->i_rsv_window = NULL; ei->vfs_inode.i_version = 1; return &ei->vfs_inode; } @@ -485,6 +485,7 @@ static void destroy_inodecache(void) static void ext3_clear_inode(struct inode *inode) { + struct ext3_reserve_window_node *rsv = EXT3_I(inode)->i_rsv_window; #ifdef CONFIG_EXT3_FS_POSIX_ACL if (EXT3_I(inode)->i_acl && EXT3_I(inode)->i_acl != EXT3_ACL_NOT_CACHED) { @@ -498,6 +499,8 @@ static void ext3_clear_inode(struct inod } #endif ext3_discard_reservation(inode); + EXT3_I(inode)->i_rsv_window = NULL; + kfree(rsv); } #ifdef CONFIG_QUOTA diff -puN include/linux/ext3_fs.h~ext3-dynamic-allocating-block-reservation-info include/linux/ext3_fs.h --- 25/include/linux/ext3_fs.h~ext3-dynamic-allocating-block-reservation-info 2005-03-23 01:33:48.000000000 -0800 +++ 25-akpm/include/linux/ext3_fs.h 2005-03-23 01:34:02.000000000 -0800 @@ -725,6 +725,7 @@ extern struct ext3_group_desc * ext3_get unsigned int block_group, struct buffer_head ** bh); extern int ext3_should_retry_alloc(struct super_block *sb, int *retries); +extern void ext3_alloc_init_reservation(struct inode *); extern void ext3_rsv_window_add(struct super_block *sb, struct ext3_reserve_window_node *rsv); /* dir.c */ diff -puN include/linux/ext3_fs_i.h~ext3-dynamic-allocating-block-reservation-info include/linux/ext3_fs_i.h --- 25/include/linux/ext3_fs_i.h~ext3-dynamic-allocating-block-reservation-info 2005-03-23 01:33:48.000000000 -0800 +++ 25-akpm/include/linux/ext3_fs_i.h 2005-03-23 01:34:02.000000000 -0800 @@ -75,8 +75,8 @@ struct ext3_inode_info { * allocation when we detect linearly ascending requests. */ __u32 i_next_alloc_goal; - /* block reservation window */ - struct ext3_reserve_window_node i_rsv_window; + /* block reservation info */ + struct ext3_reserve_window_node *i_rsv_window; __u32 i_dir_start_lookup; #ifdef CONFIG_EXT3_FS_XATTR _