From: Mingming Cao <cmm@us.ibm.com>

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 <cmm@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 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
_