[PATCH] ext3: move goal logical block into block allocation info structure

Moved i_next_alloc_block and i_next_goal_block out from ext3_inod_info, and
put it together with the reservation structure into the
ext3_block_alloc_info structure, and dynamically allocate that structure
whenever need to allocation a block.  This is also apply for noreservation
mount.  Also cleanup ext3_find_goal() code.

Signed-off-by: Mingming Cao <cmm@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
index ff3fc6b..ccd632f 100644
--- a/fs/ext3/balloc.c
+++ b/fs/ext3/balloc.c
@@ -259,28 +259,47 @@
 	/* 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)
+void ext3_init_block_alloc_info(struct inode *inode)
 {
 	struct ext3_inode_info *ei = EXT3_I(inode);
-	struct ext3_reserve_window_node *rsv = ei->i_rsv_window;
+	struct ext3_block_alloc_info *block_i = ei->i_block_alloc_info;
+	struct super_block *sb = inode->i_sb;
 
-	rsv = kmalloc(sizeof(*rsv), GFP_NOFS);
-	if (rsv) {
+	block_i = kmalloc(sizeof(*block_i), GFP_NOFS);
+	if (block_i) {
+		struct ext3_reserve_window_node *rsv = &block_i->rsv_window_node;
+
 		rsv->rsv_start = EXT3_RESERVE_WINDOW_NOT_ALLOCATED;
 		rsv->rsv_end = EXT3_RESERVE_WINDOW_NOT_ALLOCATED;
-		rsv->rsv_goal_size = EXT3_DEFAULT_RESERVE_BLOCKS;
+
+	 	/*
+		 * if filesystem is mounted with NORESERVATION, the goal
+		 * reservation window size is set to zero to indicate
+		 * block reservation is off
+		 */
+		if (!test_opt(sb, RESERVATION))
+			rsv->rsv_goal_size = 0;
+		else
+			rsv->rsv_goal_size = EXT3_DEFAULT_RESERVE_BLOCKS;
 		rsv->rsv_alloc_hit = 0;
+		block_i->last_alloc_logical_block = 0;
+		block_i->last_alloc_physical_block = 0;
 	}
-	ei->i_rsv_window = rsv;
+	ei->i_block_alloc_info = block_i;
 }
 
 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_block_alloc_info *block_i = ei->i_block_alloc_info;
+	struct ext3_reserve_window_node *rsv;
 	spinlock_t *rsv_lock = &EXT3_SB(inode->i_sb)->s_rsv_window_lock;
 
-	if (rsv && !rsv_is_empty(&rsv->rsv_window)) {
+	if (!block_i)
+		return;
+
+	rsv = &block_i->rsv_window_node;
+	if (!rsv_is_empty(&rsv->rsv_window)) {
 		spin_lock(rsv_lock);
 		if (!rsv_is_empty(&rsv->rsv_window))
 			rsv_window_remove(inode->i_sb, rsv);
@@ -1161,7 +1180,7 @@
 	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_block_alloc_info *block_i;
 	unsigned short windowsz = 0;
 #ifdef EXT3FS_DEBUG
 	static int goal_hits, goal_attempts;
@@ -1194,8 +1213,9 @@
 	 * command EXT3_IOC_SETRSVSZ to set the window size to 0 to turn off
 	 * reservation on that particular file)
 	 */
-	if (rsv && ((windowsz = rsv->rsv_goal_size) > 0))
-		my_rsv = rsv;
+	block_i = EXT3_I(inode)->i_block_alloc_info;
+	if (block_i && ((windowsz = block_i->rsv_window_node.rsv_goal_size) > 0))
+		my_rsv = &block_i->rsv_window_node;
 
 	if (!ext3_has_free_blocks(sbi)) {
 		*errp = -ENOSPC;
diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c
index bf2cb4e..1e6f3ea 100644
--- a/fs/ext3/ialloc.c
+++ b/fs/ext3/ialloc.c
@@ -562,8 +562,6 @@
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
 
 	memset(ei->i_data, 0, sizeof(ei->i_data));
-	ei->i_next_alloc_block = 0;
-	ei->i_next_alloc_goal = 0;
 	ei->i_dir_start_lookup = 0;
 	ei->i_disksize = 0;
 
@@ -581,7 +579,7 @@
 	ei->i_file_acl = 0;
 	ei->i_dir_acl = 0;
 	ei->i_dtime = 0;
-	ei->i_rsv_window = NULL;
+	ei->i_block_alloc_info = NULL;
 	ei->i_block_group = group;
 
 	ext3_set_inode_flags(inode);
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 55531ed..040eb28 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -462,26 +462,22 @@
 static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4],
 			  Indirect *partial, unsigned long *goal)
 {
-	struct ext3_inode_info *ei = EXT3_I(inode);
-	/* Writer: ->i_next_alloc* */
-	if ((block == ei->i_next_alloc_block + 1)&& ei->i_next_alloc_goal) {
-		ei->i_next_alloc_block++;
-		ei->i_next_alloc_goal++;
-	}
-	/* Writer: end */
-	/* Reader: pointers, ->i_next_alloc* */
-	if (verify_chain(chain, partial)) {
-		/*
-		 * try the heuristic for sequential allocation,
-		 * failing that at least try to get decent locality.
-		 */
-		if (block == ei->i_next_alloc_block)
-			*goal = ei->i_next_alloc_goal;
-		if (!*goal)
-			*goal = ext3_find_near(inode, partial);
+	struct ext3_block_alloc_info *block_i =  EXT3_I(inode)->i_block_alloc_info;
+
+	/*
+	 * try the heuristic for sequential allocation,
+	 * failing that at least try to get decent locality.
+	 */
+	if (block_i && (block == block_i->last_alloc_logical_block + 1)
+		&& (block_i->last_alloc_physical_block != 0)) {
+		*goal = block_i->last_alloc_physical_block + 1;
 		return 0;
 	}
-	/* Reader: end */
+
+	if (verify_chain(chain, partial)) {
+		*goal = ext3_find_near(inode, partial);
+		return 0;
+	}
 	return -EAGAIN;
 }
 
@@ -599,7 +595,7 @@
 {
 	int i;
 	int err = 0;
-	struct ext3_inode_info *ei = EXT3_I(inode);
+	struct ext3_block_alloc_info *block_i = EXT3_I(inode)->i_block_alloc_info;
 
 	/*
 	 * If we're splicing into a [td]indirect block (as opposed to the
@@ -614,7 +610,6 @@
 	}
 	/* Verify that place we are splicing to is still there and vacant */
 
-	/* Writer: pointers, ->i_next_alloc* */
 	if (!verify_chain(chain, where-1) || *where->p)
 		/* Writer: end */
 		goto changed;
@@ -622,9 +617,16 @@
 	/* That's it */
 
 	*where->p = where->key;
-	ei->i_next_alloc_block = block;
-	ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);
-	/* Writer: end */
+
+	/*
+	 * update the most recently allocated logical & physical block
+	 * in i_block_alloc_info, to assist find the proper goal block for next
+	 * allocation
+	 */
+	if (block_i) {
+		block_i->last_alloc_logical_block = block;
+		block_i->last_alloc_physical_block = le32_to_cpu(where[num-1].key);
+	}
 
 	/* We are done with atomic stuff, now do the rest of housekeeping */
 
@@ -708,7 +710,6 @@
 	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);
 
@@ -755,9 +756,8 @@
 	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 (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info)) {
+		ext3_init_block_alloc_info(inode);
 	}
 
 	if (ext3_find_goal(inode, iblock, chain, partial, &goal) < 0) {
@@ -2503,7 +2503,7 @@
 	ei->i_acl = EXT3_ACL_NOT_CACHED;
 	ei->i_default_acl = EXT3_ACL_NOT_CACHED;
 #endif
-	ei->i_rsv_window = NULL;
+	ei->i_block_alloc_info = NULL;
 
 	if (__ext3_get_inode_loc(inode, &iloc, 0))
 		goto bad_inode;
@@ -2524,8 +2524,6 @@
 	inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0;
 
 	ei->i_state = 0;
-	ei->i_next_alloc_block = 0;
-	ei->i_next_alloc_goal = 0;
 	ei->i_dir_start_lookup = 0;
 	ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
 	/* We now have enough fields to check if the inode was active or not.
diff --git a/fs/ext3/ioctl.c b/fs/ext3/ioctl.c
index 8a07bb0..706d686 100644
--- a/fs/ext3/ioctl.c
+++ b/fs/ext3/ioctl.c
@@ -155,8 +155,8 @@
 	case EXT3_IOC_GETRSVSZ:
 		if (test_opt(inode->i_sb, RESERVATION)
 			&& S_ISREG(inode->i_mode)
-			&& ei->i_rsv_window) {
-			rsv_window_size = ei->i_rsv_window->rsv_goal_size;
+			&& ei->i_block_alloc_info) {
+			rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;
 			return put_user(rsv_window_size, (int __user *)arg);
 		}
 		return -ENOTTY;
@@ -182,11 +182,13 @@
 		 * before set the window size
 		 */
 		down(&ei->truncate_sem);
-		if (!ei->i_rsv_window)
-			ext3_alloc_init_reservation(inode);
+		if (!ei->i_block_alloc_info)
+			ext3_init_block_alloc_info(inode);
 
-		if (ei->i_rsv_window)
-			ei->i_rsv_window->rsv_goal_size = rsv_window_size;
+		if (ei->i_block_alloc_info){
+			struct ext3_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node;
+			rsv->rsv_goal_size = rsv_window_size;
+		}
 		up(&ei->truncate_sem);
 		return 0;
 	}
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index e8b45bc..53b0542 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -441,7 +441,7 @@
 	ei->i_acl = EXT3_ACL_NOT_CACHED;
 	ei->i_default_acl = EXT3_ACL_NOT_CACHED;
 #endif
-	ei->i_rsv_window = NULL;
+	ei->i_block_alloc_info = NULL;
 	ei->vfs_inode.i_version = 1;
 	return &ei->vfs_inode;
 }
@@ -485,7 +485,7 @@
 
 static void ext3_clear_inode(struct inode *inode)
 {
-	struct ext3_reserve_window_node *rsv = EXT3_I(inode)->i_rsv_window;
+	struct ext3_block_alloc_info *rsv = EXT3_I(inode)->i_block_alloc_info;
 #ifdef CONFIG_EXT3_FS_POSIX_ACL
        if (EXT3_I(inode)->i_acl &&
            EXT3_I(inode)->i_acl != EXT3_ACL_NOT_CACHED) {
@@ -499,7 +499,7 @@
        }
 #endif
 	ext3_discard_reservation(inode);
-	EXT3_I(inode)->i_rsv_window = NULL;
+	EXT3_I(inode)->i_block_alloc_info = NULL;
 	kfree(rsv);
 }
 
diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h
index 45d15e5..74ad317 100644
--- a/include/linux/ext3_fs.h
+++ b/include/linux/ext3_fs.h
@@ -725,7 +725,7 @@
 						    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_init_block_alloc_info(struct inode *);
 extern void ext3_rsv_window_add(struct super_block *sb, struct ext3_reserve_window_node *rsv);
 
 /* dir.c */
diff --git a/include/linux/ext3_fs_i.h b/include/linux/ext3_fs_i.h
index 45a81d3..2914f7b 100644
--- a/include/linux/ext3_fs_i.h
+++ b/include/linux/ext3_fs_i.h
@@ -32,6 +32,26 @@
 	struct ext3_reserve_window	rsv_window;
 };
 
+struct ext3_block_alloc_info {
+	/* information about reservation window */
+	struct ext3_reserve_window_node	rsv_window_node;
+	/*
+	 * was i_next_alloc_block in ext3_inode_info
+	 * is the logical (file-relative) number of the
+	 * most-recently-allocated block in this file.
+	 * We use this for detecting linearly ascending allocation requests.
+	 */
+	__u32                   last_alloc_logical_block;
+	/*
+	 * Was i_next_alloc_goal in ext3_inode_info
+	 * is the *physical* companion to i_next_alloc_block.
+	 * it the the physical block number of the block which was most-recentl
+	 * allocated to this file.  This give us the goal (target) for the next
+	 * allocation when we detect linearly ascending requests.
+	 */
+	__u32                   last_alloc_physical_block;
+};
+
 #define rsv_start rsv_window._rsv_start
 #define rsv_end rsv_window._rsv_end
 
@@ -60,22 +80,8 @@
 	__u32	i_block_group;
 	__u32	i_state;		/* Dynamic state flags for ext3 */
 
-	/*
-	 * i_next_alloc_block is the logical (file-relative) number of the
-	 * most-recently-allocated block in this file.  Yes, it is misnamed.
-	 * We use this for detecting linearly ascending allocation requests.
-	 */
-	__u32	i_next_alloc_block;
-
-	/*
-	 * i_next_alloc_goal is the *physical* companion to i_next_alloc_block.
-	 * it the the physical block number of the block which was most-recently
-	 * allocated to this file.  This give us the goal (target) for the next
-	 * allocation when we detect linearly ascending requests.
-	 */
-	__u32	i_next_alloc_goal;
 	/* block reservation info */
-	struct ext3_reserve_window_node *i_rsv_window;
+	struct ext3_block_alloc_info *i_block_alloc_info;
 
 	__u32	i_dir_start_lookup;
 #ifdef CONFIG_EXT3_FS_XATTR