ext2 reservations
Val's cross-port of the ext3 reservations code into ext2. [mbligh@mbligh.org: Small type error for printk [akpm@linux-foundation.org: fix types, sync with ext3] [mbligh@mbligh.org: Bring ext2 reservations code in line with latest ext3] [akpm@linux-foundation.org: kill noisy printk] [akpm@linux-foundation.org: remember to dirty the gdp's block] [akpm@linux-foundation.org: cross-port the missed5dea5176e5] [akpm@linux-foundation.org: cross-porte6022603b9] [akpm@linux-foundation.org: Port the omitted08fb306fe6] [akpm@linux-foundation.org: Backport the missed20acaa18d0] [akpm@linux-foundation.org: fixes] [cmm@us.ibm.com: fix reservation extension] [bunk@stusta.de: make ext2_get_blocks() static] [hugh@veritas.com: fix hang] [hugh@veritas.com: ext2_new_blocks should reset the reservation window size] [hugh@veritas.com: ext2 balloc: fix off-by-one against rsv_end] [hugh@veritas.com: grp_goal 0 is a genuine goal (unlike -1), so ext2_try_to_allocate_with_rsv should treat it as such] [hugh@veritas.com: rbtree usage cleanup] [pbadari@us.ibm.com: Fix for ext2 reservation] [bunk@kernel.org: remove fs/ext2/balloc.c:reserve_blocks()] [hugh@veritas.com: ext2 balloc: use io_error label] Cc: "Martin J. Bligh" <mbligh@mbligh.org> Cc: Valerie Henson <val_henson@linux.intel.com> Cc: Mingming Cao <cmm@us.ibm.com> Cc: Mel Gorman <mel@csn.ul.ie> Cc: Hugh Dickins <hugh@veritas.com> Signed-off-by: Adrian Bunk <bunk@stusta.de> Signed-off-by: Hugh Dickins <hugh@veritas.com> Signed-off-by: Badari Pulavarty <pbadari@us.ibm.com> Signed-off-by: Adrian Bunk <bunk@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									369f2389e7
								
							
						
					
					
						commit
						a686cd898b
					
				
							
								
								
									
										1337
									
								
								fs/ext2/balloc.c
									
									
									
									
									
								
							
							
						
						
									
										1337
									
								
								fs/ext2/balloc.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -33,22 +33,9 @@ struct ext2_inode_info { | ||||
| 	 */ | ||||
| 	__u32	i_block_group; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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; | ||||
| 	/* block reservation info */ | ||||
| 	struct ext2_block_alloc_info *i_block_alloc_info; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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; | ||||
| 	__u32	i_prealloc_block; | ||||
| 	__u32	i_prealloc_count; | ||||
| 	__u32	i_dir_start_lookup; | ||||
| #ifdef CONFIG_EXT2_FS_XATTR | ||||
| 	/*
 | ||||
| @ -65,7 +52,16 @@ struct ext2_inode_info { | ||||
| 	struct posix_acl	*i_default_acl; | ||||
| #endif | ||||
| 	rwlock_t i_meta_lock; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * truncate_mutex is for serialising ext2_truncate() against | ||||
| 	 * ext2_getblock().  It also protects the internals of the inode's | ||||
| 	 * reservation data structures: ext2_reserve_window and | ||||
| 	 * ext2_reserve_window_node. | ||||
| 	 */ | ||||
| 	struct mutex truncate_mutex; | ||||
| 	struct inode	vfs_inode; | ||||
| 	struct list_head i_orphan;	/* unlinked but open inodes */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
| @ -91,8 +87,9 @@ static inline struct ext2_inode_info *EXT2_I(struct inode *inode) | ||||
| /* balloc.c */ | ||||
| extern int ext2_bg_has_super(struct super_block *sb, int group); | ||||
| extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group); | ||||
| extern int ext2_new_block (struct inode *, unsigned long, | ||||
| 			   __u32 *, __u32 *, int *); | ||||
| extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *); | ||||
| extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long, | ||||
| 				unsigned long *, int *); | ||||
| extern void ext2_free_blocks (struct inode *, unsigned long, | ||||
| 			      unsigned long); | ||||
| extern unsigned long ext2_count_free_blocks (struct super_block *); | ||||
| @ -101,6 +98,10 @@ extern void ext2_check_blocks_bitmap (struct super_block *); | ||||
| extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, | ||||
| 						    unsigned int block_group, | ||||
| 						    struct buffer_head ** bh); | ||||
| extern void ext2_discard_reservation (struct inode *); | ||||
| extern int ext2_should_retry_alloc(struct super_block *sb, int *retries); | ||||
| extern void ext2_init_block_alloc_info(struct inode *); | ||||
| extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv); | ||||
| 
 | ||||
| /* dir.c */ | ||||
| extern int ext2_add_link (struct dentry *, struct inode *); | ||||
| @ -128,7 +129,6 @@ extern int ext2_write_inode (struct inode *, int); | ||||
| extern void ext2_put_inode (struct inode *); | ||||
| extern void ext2_delete_inode (struct inode *); | ||||
| extern int ext2_sync_inode (struct inode *); | ||||
| extern void ext2_discard_prealloc (struct inode *); | ||||
| extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); | ||||
| extern void ext2_truncate (struct inode *); | ||||
| extern int ext2_setattr (struct dentry *, struct iattr *); | ||||
|  | ||||
| @ -30,8 +30,11 @@ | ||||
|  */ | ||||
| static int ext2_release_file (struct inode * inode, struct file * filp) | ||||
| { | ||||
| 	if (filp->f_mode & FMODE_WRITE) | ||||
| 		ext2_discard_prealloc (inode); | ||||
| 	if (filp->f_mode & FMODE_WRITE) { | ||||
| 		mutex_lock(&EXT2_I(inode)->truncate_mutex); | ||||
| 		ext2_discard_reservation(inode); | ||||
| 		mutex_unlock(&EXT2_I(inode)->truncate_mutex); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -581,11 +581,8 @@ got: | ||||
| 	ei->i_file_acl = 0; | ||||
| 	ei->i_dir_acl = 0; | ||||
| 	ei->i_dtime = 0; | ||||
| 	ei->i_block_alloc_info = NULL; | ||||
| 	ei->i_block_group = group; | ||||
| 	ei->i_next_alloc_block = 0; | ||||
| 	ei->i_next_alloc_goal = 0; | ||||
| 	ei->i_prealloc_block = 0; | ||||
| 	ei->i_prealloc_count = 0; | ||||
| 	ei->i_dir_start_lookup = 0; | ||||
| 	ei->i_state = EXT2_STATE_NEW; | ||||
| 	ext2_set_inode_flags(inode); | ||||
|  | ||||
							
								
								
									
										522
									
								
								fs/ext2/inode.c
									
									
									
									
									
								
							
							
						
						
									
										522
									
								
								fs/ext2/inode.c
									
									
									
									
									
								
							| @ -53,19 +53,6 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode) | ||||
| 		inode->i_blocks - ea_blocks == 0); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Called at each iput(). | ||||
|  * | ||||
|  * The inode may be "bad" if ext2_read_inode() saw an error from | ||||
|  * ext2_get_inode(), so we need to check that to avoid freeing random disk | ||||
|  * blocks. | ||||
|  */ | ||||
| void ext2_put_inode(struct inode *inode) | ||||
| { | ||||
| 	if (!is_bad_inode(inode)) | ||||
| 		ext2_discard_prealloc(inode); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Called at the last iput() if i_nlink is zero. | ||||
|  */ | ||||
| @ -89,61 +76,6 @@ no_delete: | ||||
| 	clear_inode(inode);	/* We must guarantee clearing of inode... */ | ||||
| } | ||||
| 
 | ||||
| void ext2_discard_prealloc (struct inode * inode) | ||||
| { | ||||
| #ifdef EXT2_PREALLOCATE | ||||
| 	struct ext2_inode_info *ei = EXT2_I(inode); | ||||
| 	write_lock(&ei->i_meta_lock); | ||||
| 	if (ei->i_prealloc_count) { | ||||
| 		unsigned short total = ei->i_prealloc_count; | ||||
| 		unsigned long block = ei->i_prealloc_block; | ||||
| 		ei->i_prealloc_count = 0; | ||||
| 		ei->i_prealloc_block = 0; | ||||
| 		write_unlock(&ei->i_meta_lock); | ||||
| 		ext2_free_blocks (inode, block, total); | ||||
| 		return; | ||||
| 	} else | ||||
| 		write_unlock(&ei->i_meta_lock); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err) | ||||
| { | ||||
| #ifdef EXT2FS_DEBUG | ||||
| 	static unsigned long alloc_hits, alloc_attempts; | ||||
| #endif | ||||
| 	unsigned long result; | ||||
| 
 | ||||
| 
 | ||||
| #ifdef EXT2_PREALLOCATE | ||||
| 	struct ext2_inode_info *ei = EXT2_I(inode); | ||||
| 	write_lock(&ei->i_meta_lock); | ||||
| 	if (ei->i_prealloc_count && | ||||
| 	    (goal == ei->i_prealloc_block || goal + 1 == ei->i_prealloc_block)) | ||||
| 	{ | ||||
| 		result = ei->i_prealloc_block++; | ||||
| 		ei->i_prealloc_count--; | ||||
| 		write_unlock(&ei->i_meta_lock); | ||||
| 		ext2_debug ("preallocation hit (%lu/%lu).\n", | ||||
| 			    ++alloc_hits, ++alloc_attempts); | ||||
| 	} else { | ||||
| 		write_unlock(&ei->i_meta_lock); | ||||
| 		ext2_discard_prealloc (inode); | ||||
| 		ext2_debug ("preallocation miss (%lu/%lu).\n", | ||||
| 			    alloc_hits, ++alloc_attempts); | ||||
| 		if (S_ISREG(inode->i_mode)) | ||||
| 			result = ext2_new_block (inode, goal,  | ||||
| 				 &ei->i_prealloc_count, | ||||
| 				 &ei->i_prealloc_block, err); | ||||
| 		else | ||||
| 			result = ext2_new_block(inode, goal, NULL, NULL, err); | ||||
| 	} | ||||
| #else | ||||
| 	result = ext2_new_block (inode, goal, 0, 0, err); | ||||
| #endif | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| typedef struct { | ||||
| 	__le32	*p; | ||||
| 	__le32	key; | ||||
| @ -228,7 +160,8 @@ static int ext2_block_to_path(struct inode *inode, | ||||
| 		ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big"); | ||||
| 	} | ||||
| 	if (boundary) | ||||
| 		*boundary = (i_block & (ptrs - 1)) == (final - 1); | ||||
| 		*boundary = final - 1 - (i_block & (ptrs - 1)); | ||||
| 
 | ||||
| 	return n; | ||||
| } | ||||
| 
 | ||||
| @ -355,39 +288,129 @@ static unsigned long ext2_find_near(struct inode *inode, Indirect *ind) | ||||
|  *	@block:  block we want | ||||
|  *	@chain:  chain of indirect blocks | ||||
|  *	@partial: pointer to the last triple within a chain | ||||
|  *	@goal:	place to store the result. | ||||
|  * | ||||
|  *	Normally this function find the prefered place for block allocation, | ||||
|  *	stores it in *@goal and returns zero. If the branch had been changed | ||||
|  *	under us we return -EAGAIN. | ||||
|  *	Returns preferred place for a block (the goal). | ||||
|  */ | ||||
| 
 | ||||
| static inline int ext2_find_goal(struct inode *inode, | ||||
| 				 long block, | ||||
| 				 Indirect chain[4], | ||||
| 				 Indirect *partial, | ||||
| 				 unsigned long *goal) | ||||
| 				 Indirect *partial) | ||||
| { | ||||
| 	struct ext2_inode_info *ei = EXT2_I(inode); | ||||
| 	write_lock(&ei->i_meta_lock); | ||||
| 	if ((block == ei->i_next_alloc_block + 1) && ei->i_next_alloc_goal) { | ||||
| 		ei->i_next_alloc_block++; | ||||
| 		ei->i_next_alloc_goal++; | ||||
| 	}  | ||||
| 	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 = ext2_find_near(inode, partial); | ||||
| 		write_unlock(&ei->i_meta_lock); | ||||
| 		return 0; | ||||
| 	struct ext2_block_alloc_info *block_i; | ||||
| 
 | ||||
| 	block_i = EXT2_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)) { | ||||
| 		return block_i->last_alloc_physical_block + 1; | ||||
| 	} | ||||
| 	write_unlock(&ei->i_meta_lock); | ||||
| 	return -EAGAIN; | ||||
| 
 | ||||
| 	return ext2_find_near(inode, partial); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	ext2_blks_to_allocate: Look up the block map and count the number | ||||
|  *	of direct blocks need to be allocated for the given branch. | ||||
|  * | ||||
|  * 	@branch: chain of indirect blocks | ||||
|  *	@k: number of blocks need for indirect blocks | ||||
|  *	@blks: number of data blocks to be mapped. | ||||
|  *	@blocks_to_boundary:  the offset in the indirect block | ||||
|  * | ||||
|  *	return the total number of blocks to be allocate, including the | ||||
|  *	direct and indirect blocks. | ||||
|  */ | ||||
| static int | ||||
| ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks, | ||||
| 		int blocks_to_boundary) | ||||
| { | ||||
| 	unsigned long count = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Simple case, [t,d]Indirect block(s) has not allocated yet | ||||
| 	 * then it's clear blocks on that path have not allocated | ||||
| 	 */ | ||||
| 	if (k > 0) { | ||||
| 		/* right now don't hanel cross boundary allocation */ | ||||
| 		if (blks < blocks_to_boundary + 1) | ||||
| 			count += blks; | ||||
| 		else | ||||
| 			count += blocks_to_boundary + 1; | ||||
| 		return count; | ||||
| 	} | ||||
| 
 | ||||
| 	count++; | ||||
| 	while (count < blks && count <= blocks_to_boundary | ||||
| 		&& le32_to_cpu(*(branch[0].p + count)) == 0) { | ||||
| 		count++; | ||||
| 	} | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	ext2_alloc_blocks: multiple allocate blocks needed for a branch | ||||
|  *	@indirect_blks: the number of blocks need to allocate for indirect | ||||
|  *			blocks | ||||
|  * | ||||
|  *	@new_blocks: on return it will store the new block numbers for | ||||
|  *	the indirect blocks(if needed) and the first direct block, | ||||
|  *	@blks:	on return it will store the total number of allocated | ||||
|  *		direct blocks | ||||
|  */ | ||||
| static int ext2_alloc_blocks(struct inode *inode, | ||||
| 			ext2_fsblk_t goal, int indirect_blks, int blks, | ||||
| 			ext2_fsblk_t new_blocks[4], int *err) | ||||
| { | ||||
| 	int target, i; | ||||
| 	unsigned long count = 0; | ||||
| 	int index = 0; | ||||
| 	ext2_fsblk_t current_block = 0; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Here we try to allocate the requested multiple blocks at once, | ||||
| 	 * on a best-effort basis. | ||||
| 	 * To build a branch, we should allocate blocks for | ||||
| 	 * the indirect blocks(if not allocated yet), and at least | ||||
| 	 * the first direct block of this branch.  That's the | ||||
| 	 * minimum number of blocks need to allocate(required) | ||||
| 	 */ | ||||
| 	target = blks + indirect_blks; | ||||
| 
 | ||||
| 	while (1) { | ||||
| 		count = target; | ||||
| 		/* allocating blocks for indirect blocks and direct blocks */ | ||||
| 		current_block = ext2_new_blocks(inode,goal,&count,err); | ||||
| 		if (*err) | ||||
| 			goto failed_out; | ||||
| 
 | ||||
| 		target -= count; | ||||
| 		/* allocate blocks for indirect blocks */ | ||||
| 		while (index < indirect_blks && count) { | ||||
| 			new_blocks[index++] = current_block++; | ||||
| 			count--; | ||||
| 		} | ||||
| 
 | ||||
| 		if (count > 0) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	/* save the new block number for the first direct block */ | ||||
| 	new_blocks[index] = current_block; | ||||
| 
 | ||||
| 	/* total number of blocks allocated for direct blocks */ | ||||
| 	ret = count; | ||||
| 	*err = 0; | ||||
| 	return ret; | ||||
| failed_out: | ||||
| 	for (i = 0; i <index; i++) | ||||
| 		ext2_free_blocks(inode, new_blocks[i], 1); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| @ -416,39 +439,49 @@ static inline int ext2_find_goal(struct inode *inode, | ||||
|  */ | ||||
| 
 | ||||
| static int ext2_alloc_branch(struct inode *inode, | ||||
| 			     int num, | ||||
| 			     unsigned long goal, | ||||
| 			     int *offsets, | ||||
| 			     Indirect *branch) | ||||
| 			int indirect_blks, int *blks, ext2_fsblk_t goal, | ||||
| 			int *offsets, Indirect *branch) | ||||
| { | ||||
| 	int blocksize = inode->i_sb->s_blocksize; | ||||
| 	int n = 0; | ||||
| 	int err; | ||||
| 	int i; | ||||
| 	int parent = ext2_alloc_block(inode, goal, &err); | ||||
| 	int i, n = 0; | ||||
| 	int err = 0; | ||||
| 	struct buffer_head *bh; | ||||
| 	int num; | ||||
| 	ext2_fsblk_t new_blocks[4]; | ||||
| 	ext2_fsblk_t current_block; | ||||
| 
 | ||||
| 	branch[0].key = cpu_to_le32(parent); | ||||
| 	if (parent) for (n = 1; n < num; n++) { | ||||
| 		struct buffer_head *bh; | ||||
| 		/* Allocate the next block */ | ||||
| 		int nr = ext2_alloc_block(inode, parent, &err); | ||||
| 		if (!nr) | ||||
| 			break; | ||||
| 		branch[n].key = cpu_to_le32(nr); | ||||
| 	num = ext2_alloc_blocks(inode, goal, indirect_blks, | ||||
| 				*blks, new_blocks, &err); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	branch[0].key = cpu_to_le32(new_blocks[0]); | ||||
| 	/*
 | ||||
| 	 * metadata blocks and data blocks are allocated. | ||||
| 	 */ | ||||
| 	for (n = 1; n <= indirect_blks;  n++) { | ||||
| 		/*
 | ||||
| 		 * Get buffer_head for parent block, zero it out and set  | ||||
| 		 * the pointer to new one, then send parent to disk. | ||||
| 		 * Get buffer_head for parent block, zero it out | ||||
| 		 * and set the pointer to new one, then send | ||||
| 		 * parent to disk. | ||||
| 		 */ | ||||
| 		bh = sb_getblk(inode->i_sb, parent); | ||||
| 		if (!bh) { | ||||
| 			err = -EIO; | ||||
| 			break; | ||||
| 		} | ||||
| 		bh = sb_getblk(inode->i_sb, new_blocks[n-1]); | ||||
| 		branch[n].bh = bh; | ||||
| 		lock_buffer(bh); | ||||
| 		memset(bh->b_data, 0, blocksize); | ||||
| 		branch[n].bh = bh; | ||||
| 		branch[n].p = (__le32 *) bh->b_data + offsets[n]; | ||||
| 		branch[n].key = cpu_to_le32(new_blocks[n]); | ||||
| 		*branch[n].p = branch[n].key; | ||||
| 		if ( n == indirect_blks) { | ||||
| 			current_block = new_blocks[n]; | ||||
| 			/*
 | ||||
| 			 * End of chain, update the last new metablock of | ||||
| 			 * the chain to point to the new allocated | ||||
| 			 * data blocks numbers | ||||
| 			 */ | ||||
| 			for (i=1; i < num; i++) | ||||
| 				*(branch[n].p + i) = cpu_to_le32(++current_block); | ||||
| 		} | ||||
| 		set_buffer_uptodate(bh); | ||||
| 		unlock_buffer(bh); | ||||
| 		mark_buffer_dirty_inode(bh, inode); | ||||
| @ -458,77 +491,68 @@ static int ext2_alloc_branch(struct inode *inode, | ||||
| 		 */ | ||||
| 		if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) | ||||
| 			sync_dirty_buffer(bh); | ||||
| 		parent = nr; | ||||
| 	} | ||||
| 	if (n == num) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Allocation failed, free what we already allocated */ | ||||
| 	for (i = 1; i < n; i++) | ||||
| 		bforget(branch[i].bh); | ||||
| 	for (i = 0; i < n; i++) | ||||
| 		ext2_free_blocks(inode, le32_to_cpu(branch[i].key), 1); | ||||
| 	*blks = num; | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  *	ext2_splice_branch - splice the allocated branch onto inode. | ||||
|  *	@inode: owner | ||||
|  *	@block: (logical) number of block we are adding | ||||
|  *	@chain: chain of indirect blocks (with a missing link - see | ||||
|  *		ext2_alloc_branch) | ||||
|  *	@where: location of missing link | ||||
|  *	@num:   number of blocks we are adding | ||||
|  * ext2_splice_branch - splice the allocated branch onto inode. | ||||
|  * @inode: owner | ||||
|  * @block: (logical) number of block we are adding | ||||
|  * @chain: chain of indirect blocks (with a missing link - see | ||||
|  *	ext2_alloc_branch) | ||||
|  * @where: location of missing link | ||||
|  * @num:   number of indirect blocks we are adding | ||||
|  * @blks:  number of direct blocks we are adding | ||||
|  * | ||||
|  *	This function verifies that chain (up to the missing link) had not | ||||
|  *	changed, fills the missing link and does all housekeeping needed in | ||||
|  *	inode (->i_blocks, etc.). In case of success we end up with the full | ||||
|  *	chain to new block and return 0. Otherwise (== chain had been changed) | ||||
|  *	we free the new blocks (forgetting their buffer_heads, indeed) and | ||||
|  *	return -EAGAIN. | ||||
|  * This function fills the missing link and does all housekeeping needed in | ||||
|  * inode (->i_blocks, etc.). In case of success we end up with the full | ||||
|  * chain to new block and return 0. | ||||
|  */ | ||||
| 
 | ||||
| static inline int ext2_splice_branch(struct inode *inode, | ||||
| 				     long block, | ||||
| 				     Indirect chain[4], | ||||
| 				     Indirect *where, | ||||
| 				     int num) | ||||
| static void ext2_splice_branch(struct inode *inode, | ||||
| 			long block, Indirect *where, int num, int blks) | ||||
| { | ||||
| 	struct ext2_inode_info *ei = EXT2_I(inode); | ||||
| 	int i; | ||||
| 	struct ext2_block_alloc_info *block_i; | ||||
| 	ext2_fsblk_t current_block; | ||||
| 
 | ||||
| 	/* Verify that place we are splicing to is still there and vacant */ | ||||
| 
 | ||||
| 	write_lock(&ei->i_meta_lock); | ||||
| 	if (!verify_chain(chain, where-1) || *where->p) | ||||
| 		goto changed; | ||||
| 	block_i = EXT2_I(inode)->i_block_alloc_info; | ||||
| 
 | ||||
| 	/* XXX LOCKING probably should have i_meta_lock ?*/ | ||||
| 	/* 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); | ||||
| 
 | ||||
| 	write_unlock(&ei->i_meta_lock); | ||||
| 	/*
 | ||||
| 	 * Update the host buffer_head or inode to point to more just allocated | ||||
| 	 * direct blocks blocks | ||||
| 	 */ | ||||
| 	if (num == 0 && blks > 1) { | ||||
| 		current_block = le32_to_cpu(where->key) + 1; | ||||
| 		for (i = 1; i < blks; i++) | ||||
| 			*(where->p + i ) = cpu_to_le32(current_block++); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * 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 + blks - 1; | ||||
| 		block_i->last_alloc_physical_block = | ||||
| 				le32_to_cpu(where[num].key) + blks - 1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We are done with atomic stuff, now do the rest of housekeeping */ | ||||
| 
 | ||||
| 	inode->i_ctime = CURRENT_TIME_SEC; | ||||
| 
 | ||||
| 	/* had we spliced it onto indirect block? */ | ||||
| 	if (where->bh) | ||||
| 		mark_buffer_dirty_inode(where->bh, inode); | ||||
| 
 | ||||
| 	inode->i_ctime = CURRENT_TIME_SEC; | ||||
| 	mark_inode_dirty(inode); | ||||
| 	return 0; | ||||
| 
 | ||||
| changed: | ||||
| 	write_unlock(&ei->i_meta_lock); | ||||
| 	for (i = 1; i < num; i++) | ||||
| 		bforget(where[i].bh); | ||||
| 	for (i = 0; i < num; i++) | ||||
| 		ext2_free_blocks(inode, le32_to_cpu(where[i].key), 1); | ||||
| 	return -EAGAIN; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -542,64 +566,99 @@ changed: | ||||
|  * That has a nice additional property: no special recovery from the failed | ||||
|  * allocations is needed - we simply release blocks and do not touch anything | ||||
|  * reachable from inode. | ||||
|  * | ||||
|  * `handle' can be NULL if create == 0. | ||||
|  * | ||||
|  * The BKL may not be held on entry here.  Be sure to take it early. | ||||
|  * return > 0, # of blocks mapped or allocated. | ||||
|  * return = 0, if plain lookup failed. | ||||
|  * return < 0, error case. | ||||
|  */ | ||||
| 
 | ||||
| int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) | ||||
| static int ext2_get_blocks(struct inode *inode, | ||||
| 			   sector_t iblock, unsigned long maxblocks, | ||||
| 			   struct buffer_head *bh_result, | ||||
| 			   int create) | ||||
| { | ||||
| 	int err = -EIO; | ||||
| 	int offsets[4]; | ||||
| 	Indirect chain[4]; | ||||
| 	Indirect *partial; | ||||
| 	unsigned long goal; | ||||
| 	int left; | ||||
| 	int boundary = 0; | ||||
| 	int depth = ext2_block_to_path(inode, iblock, offsets, &boundary); | ||||
| 	ext2_fsblk_t goal; | ||||
| 	int indirect_blks; | ||||
| 	int blocks_to_boundary = 0; | ||||
| 	int depth; | ||||
| 	struct ext2_inode_info *ei = EXT2_I(inode); | ||||
| 	int count = 0; | ||||
| 	ext2_fsblk_t first_block = 0; | ||||
| 
 | ||||
| 	depth = ext2_block_to_path(inode,iblock,offsets,&blocks_to_boundary); | ||||
| 
 | ||||
| 	if (depth == 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 		return (err); | ||||
| reread: | ||||
| 	partial = ext2_get_branch(inode, depth, offsets, chain, &err); | ||||
| 
 | ||||
| 	/* Simplest case - block found, no allocation needed */ | ||||
| 	if (!partial) { | ||||
| got_it: | ||||
| 		map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); | ||||
| 		if (boundary) | ||||
| 			set_buffer_boundary(bh_result); | ||||
| 		/* Clean up and exit */ | ||||
| 		partial = chain+depth-1; /* the whole chain */ | ||||
| 		goto cleanup; | ||||
| 		first_block = le32_to_cpu(chain[depth - 1].key); | ||||
| 		clear_buffer_new(bh_result); /* What's this do? */ | ||||
| 		count++; | ||||
| 		/*map more blocks*/ | ||||
| 		while (count < maxblocks && count <= blocks_to_boundary) { | ||||
| 			ext2_fsblk_t blk; | ||||
| 
 | ||||
| 			if (!verify_chain(chain, partial)) { | ||||
| 				/*
 | ||||
| 				 * Indirect block might be removed by | ||||
| 				 * truncate while we were reading it. | ||||
| 				 * Handling of that case: forget what we've | ||||
| 				 * got now, go to reread. | ||||
| 				 */ | ||||
| 				count = 0; | ||||
| 				goto changed; | ||||
| 			} | ||||
| 			blk = le32_to_cpu(*(chain[depth-1].p + count)); | ||||
| 			if (blk == first_block + count) | ||||
| 				count++; | ||||
| 			else | ||||
| 				break; | ||||
| 		} | ||||
| 		goto got_it; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Next simple case - plain lookup or failed read of indirect block */ | ||||
| 	if (!create || err == -EIO) { | ||||
| cleanup: | ||||
| 		while (partial > chain) { | ||||
| 			brelse(partial->bh); | ||||
| 			partial--; | ||||
| 		} | ||||
| out: | ||||
| 		return err; | ||||
| 	} | ||||
| 	if (!create || err == -EIO) | ||||
| 		goto cleanup; | ||||
| 
 | ||||
| 	mutex_lock(&ei->truncate_mutex); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Indirect block might be removed by truncate while we were | ||||
| 	 * reading it. Handling of that case (forget what we've got and | ||||
| 	 * reread) is taken out of the main path. | ||||
| 	 * Okay, we need to do block allocation.  Lazily initialize the block | ||||
| 	 * allocation info here if necessary | ||||
| 	*/ | ||||
| 	if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info)) | ||||
| 		ext2_init_block_alloc_info(inode); | ||||
| 
 | ||||
| 	goal = ext2_find_goal(inode, iblock, chain, partial); | ||||
| 
 | ||||
| 	/* the number of blocks need to allocate for [d,t]indirect blocks */ | ||||
| 	indirect_blks = (chain + depth) - partial - 1; | ||||
| 	/*
 | ||||
| 	 * Next look up the indirect map to count the totoal number of | ||||
| 	 * direct blocks to allocate for this branch. | ||||
| 	 */ | ||||
| 	if (err == -EAGAIN) | ||||
| 		goto changed; | ||||
| 	count = ext2_blks_to_allocate(partial, indirect_blks, | ||||
| 					maxblocks, blocks_to_boundary); | ||||
| 	/*
 | ||||
| 	 * XXX ???? Block out ext2_truncate while we alter the tree | ||||
| 	 */ | ||||
| 	err = ext2_alloc_branch(inode, indirect_blks, &count, goal, | ||||
| 				offsets + (partial - chain), partial); | ||||
| 
 | ||||
| 	goal = 0; | ||||
| 	if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0) | ||||
| 		goto changed; | ||||
| 
 | ||||
| 	left = (chain + depth) - partial; | ||||
| 	err = ext2_alloc_branch(inode, left, goal, | ||||
| 					offsets+(partial-chain), partial); | ||||
| 	if (err) | ||||
| 	if (err) { | ||||
| 		mutex_unlock(&ei->truncate_mutex); | ||||
| 		goto cleanup; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ext2_use_xip(inode->i_sb)) { | ||||
| 		/*
 | ||||
| @ -607,16 +666,28 @@ out: | ||||
| 		 */ | ||||
| 		err = ext2_clear_xip_target (inode, | ||||
| 			le32_to_cpu(chain[depth-1].key)); | ||||
| 		if (err) | ||||
| 		if (err) { | ||||
| 			mutex_unlock(&ei->truncate_mutex); | ||||
| 			goto cleanup; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0) | ||||
| 		goto changed; | ||||
| 
 | ||||
| 	ext2_splice_branch(inode, iblock, partial, indirect_blks, count); | ||||
| 	mutex_unlock(&ei->truncate_mutex); | ||||
| 	set_buffer_new(bh_result); | ||||
| 	goto got_it; | ||||
| 
 | ||||
| got_it: | ||||
| 	map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); | ||||
| 	if (count > blocks_to_boundary) | ||||
| 		set_buffer_boundary(bh_result); | ||||
| 	err = count; | ||||
| 	/* Clean up and exit */ | ||||
| 	partial = chain + depth - 1;	/* the whole chain */ | ||||
| cleanup: | ||||
| 	while (partial > chain) { | ||||
| 		brelse(partial->bh); | ||||
| 		partial--; | ||||
| 	} | ||||
| 	return err; | ||||
| changed: | ||||
| 	while (partial > chain) { | ||||
| 		brelse(partial->bh); | ||||
| @ -625,6 +696,19 @@ changed: | ||||
| 	goto reread; | ||||
| } | ||||
| 
 | ||||
| int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) | ||||
| { | ||||
| 	unsigned max_blocks = bh_result->b_size >> inode->i_blkbits; | ||||
| 	int ret = ext2_get_blocks(inode, iblock, max_blocks, | ||||
| 			      bh_result, create); | ||||
| 	if (ret > 0) { | ||||
| 		bh_result->b_size = (ret << inode->i_blkbits); | ||||
| 		ret = 0; | ||||
| 	} | ||||
| 	return ret; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static int ext2_writepage(struct page *page, struct writeback_control *wbc) | ||||
| { | ||||
| 	return block_write_full_page(page, ext2_get_block, wbc); | ||||
| @ -913,9 +997,10 @@ static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int de | ||||
| 		ext2_free_data(inode, p, q); | ||||
| } | ||||
| 
 | ||||
| void ext2_truncate (struct inode * inode) | ||||
| void ext2_truncate(struct inode *inode) | ||||
| { | ||||
| 	__le32 *i_data = EXT2_I(inode)->i_data; | ||||
| 	struct ext2_inode_info *ei = EXT2_I(inode); | ||||
| 	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); | ||||
| 	int offsets[4]; | ||||
| 	Indirect chain[4]; | ||||
| @ -933,8 +1018,6 @@ void ext2_truncate (struct inode * inode) | ||||
| 	if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||||
| 		return; | ||||
| 
 | ||||
| 	ext2_discard_prealloc(inode); | ||||
| 
 | ||||
| 	blocksize = inode->i_sb->s_blocksize; | ||||
| 	iblock = (inode->i_size + blocksize-1) | ||||
| 					>> EXT2_BLOCK_SIZE_BITS(inode->i_sb); | ||||
| @ -952,6 +1035,12 @@ void ext2_truncate (struct inode * inode) | ||||
| 	if (n == 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * From here we block out all ext2_get_block() callers who want to | ||||
| 	 * modify the block allocation tree. | ||||
| 	 */ | ||||
| 	mutex_lock(&ei->truncate_mutex); | ||||
| 
 | ||||
| 	if (n == 1) { | ||||
| 		ext2_free_data(inode, i_data+offsets[0], | ||||
| 					i_data + EXT2_NDIR_BLOCKS); | ||||
| @ -1004,6 +1093,10 @@ do_indirects: | ||||
| 		case EXT2_TIND_BLOCK: | ||||
| 			; | ||||
| 	} | ||||
| 
 | ||||
| 	ext2_discard_reservation(inode); | ||||
| 
 | ||||
| 	mutex_unlock(&ei->truncate_mutex); | ||||
| 	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; | ||||
| 	if (inode_needs_sync(inode)) { | ||||
| 		sync_mapping_buffers(inode->i_mapping); | ||||
| @ -1104,6 +1197,8 @@ void ext2_read_inode (struct inode * inode) | ||||
| 	ei->i_acl = EXT2_ACL_NOT_CACHED; | ||||
| 	ei->i_default_acl = EXT2_ACL_NOT_CACHED; | ||||
| #endif | ||||
| 	ei->i_block_alloc_info = NULL; | ||||
| 
 | ||||
| 	if (IS_ERR(raw_inode)) | ||||
|  		goto bad_inode; | ||||
| 
 | ||||
| @ -1145,9 +1240,6 @@ void ext2_read_inode (struct inode * inode) | ||||
| 	ei->i_dtime = 0; | ||||
| 	inode->i_generation = le32_to_cpu(raw_inode->i_generation); | ||||
| 	ei->i_state = 0; | ||||
| 	ei->i_next_alloc_block = 0; | ||||
| 	ei->i_next_alloc_goal = 0; | ||||
| 	ei->i_prealloc_count = 0; | ||||
| 	ei->i_block_group = (ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb); | ||||
| 	ei->i_dir_start_lookup = 0; | ||||
| 
 | ||||
|  | ||||
| @ -22,6 +22,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, | ||||
| { | ||||
| 	struct ext2_inode_info *ei = EXT2_I(inode); | ||||
| 	unsigned int flags; | ||||
| 	unsigned short rsv_window_size; | ||||
| 
 | ||||
| 	ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg); | ||||
| 
 | ||||
| @ -83,6 +84,50 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, | ||||
| 		inode->i_ctime = CURRENT_TIME_SEC; | ||||
| 		mark_inode_dirty(inode); | ||||
| 		return 0; | ||||
| 	case EXT2_IOC_GETRSVSZ: | ||||
| 		if (test_opt(inode->i_sb, RESERVATION) | ||||
| 			&& S_ISREG(inode->i_mode) | ||||
| 			&& 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; | ||||
| 	case EXT2_IOC_SETRSVSZ: { | ||||
| 
 | ||||
| 		if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) | ||||
| 			return -ENOTTY; | ||||
| 
 | ||||
| 		if (IS_RDONLY(inode)) | ||||
| 			return -EROFS; | ||||
| 
 | ||||
| 		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) | ||||
| 			return -EACCES; | ||||
| 
 | ||||
| 		if (get_user(rsv_window_size, (int __user *)arg)) | ||||
| 			return -EFAULT; | ||||
| 
 | ||||
| 		if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS) | ||||
| 			rsv_window_size = EXT2_MAX_RESERVE_BLOCKS; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * need to allocate reservation structure for this inode | ||||
| 		 * before set the window size | ||||
| 		 */ | ||||
| 		/*
 | ||||
| 		 * XXX What lock should protect the rsv_goal_size? | ||||
| 		 * Accessed in ext2_get_block only.  ext3 uses i_truncate. | ||||
| 		 */ | ||||
| 		mutex_lock(&ei->truncate_mutex); | ||||
| 		if (!ei->i_block_alloc_info) | ||||
| 			ext2_init_block_alloc_info(inode); | ||||
| 
 | ||||
| 		if (ei->i_block_alloc_info){ | ||||
| 			struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; | ||||
| 			rsv->rsv_goal_size = rsv_window_size; | ||||
| 		} | ||||
| 		mutex_unlock(&ei->truncate_mutex); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	default: | ||||
| 		return -ENOTTY; | ||||
| 	} | ||||
|  | ||||
| @ -149,6 +149,7 @@ static struct inode *ext2_alloc_inode(struct super_block *sb) | ||||
| 	ei->i_acl = EXT2_ACL_NOT_CACHED; | ||||
| 	ei->i_default_acl = EXT2_ACL_NOT_CACHED; | ||||
| #endif | ||||
| 	ei->i_block_alloc_info = NULL; | ||||
| 	ei->vfs_inode.i_version = 1; | ||||
| 	return &ei->vfs_inode; | ||||
| } | ||||
| @ -166,6 +167,7 @@ static void init_once(struct kmem_cache * cachep, void *foo) | ||||
| #ifdef CONFIG_EXT2_FS_XATTR | ||||
| 	init_rwsem(&ei->xattr_sem); | ||||
| #endif | ||||
| 	mutex_init(&ei->truncate_mutex); | ||||
| 	inode_init_once(&ei->vfs_inode); | ||||
| } | ||||
| 
 | ||||
| @ -188,6 +190,7 @@ static void destroy_inodecache(void) | ||||
| 
 | ||||
| static void ext2_clear_inode(struct inode *inode) | ||||
| { | ||||
| 	struct ext2_block_alloc_info *rsv = EXT2_I(inode)->i_block_alloc_info; | ||||
| #ifdef CONFIG_EXT2_FS_POSIX_ACL | ||||
| 	struct ext2_inode_info *ei = EXT2_I(inode); | ||||
| 
 | ||||
| @ -200,6 +203,10 @@ static void ext2_clear_inode(struct inode *inode) | ||||
| 		ei->i_default_acl = EXT2_ACL_NOT_CACHED; | ||||
| 	} | ||||
| #endif | ||||
| 	ext2_discard_reservation(inode); | ||||
| 	EXT2_I(inode)->i_block_alloc_info = NULL; | ||||
| 	if (unlikely(rsv)) | ||||
| 		kfree(rsv); | ||||
| } | ||||
| 
 | ||||
| static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs) | ||||
| @ -291,7 +298,6 @@ static const struct super_operations ext2_sops = { | ||||
| 	.destroy_inode	= ext2_destroy_inode, | ||||
| 	.read_inode	= ext2_read_inode, | ||||
| 	.write_inode	= ext2_write_inode, | ||||
| 	.put_inode	= ext2_put_inode, | ||||
| 	.delete_inode	= ext2_delete_inode, | ||||
| 	.put_super	= ext2_put_super, | ||||
| 	.write_super	= ext2_write_super, | ||||
| @ -379,7 +385,7 @@ enum { | ||||
| 	Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug, | ||||
| 	Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr, | ||||
| 	Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota, | ||||
| 	Opt_usrquota, Opt_grpquota | ||||
| 	Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation | ||||
| }; | ||||
| 
 | ||||
| static match_table_t tokens = { | ||||
| @ -411,6 +417,8 @@ static match_table_t tokens = { | ||||
| 	{Opt_ignore, "noquota"}, | ||||
| 	{Opt_quota, "quota"}, | ||||
| 	{Opt_usrquota, "usrquota"}, | ||||
| 	{Opt_reservation, "reservation"}, | ||||
| 	{Opt_noreservation, "noreservation"}, | ||||
| 	{Opt_err, NULL} | ||||
| }; | ||||
| 
 | ||||
| @ -543,6 +551,14 @@ static int parse_options (char * options, | ||||
| 			break; | ||||
| #endif | ||||
| 
 | ||||
| 		case Opt_reservation: | ||||
| 			set_opt(sbi->s_mount_opt, RESERVATION); | ||||
| 			printk("reservations ON\n"); | ||||
| 			break; | ||||
| 		case Opt_noreservation: | ||||
| 			clear_opt(sbi->s_mount_opt, RESERVATION); | ||||
| 			printk("reservations OFF\n"); | ||||
| 			break; | ||||
| 		case Opt_ignore: | ||||
| 			break; | ||||
| 		default: | ||||
| @ -784,6 +800,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) | ||||
| 	sbi->s_resuid = le16_to_cpu(es->s_def_resuid); | ||||
| 	sbi->s_resgid = le16_to_cpu(es->s_def_resgid); | ||||
| 	 | ||||
| 	set_opt(sbi->s_mount_opt, RESERVATION); | ||||
| 
 | ||||
| 	if (!parse_options ((char *) data, sbi)) | ||||
| 		goto failed_mount; | ||||
| 
 | ||||
| @ -965,6 +983,21 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) | ||||
| 	get_random_bytes(&sbi->s_next_generation, sizeof(u32)); | ||||
| 	spin_lock_init(&sbi->s_next_gen_lock); | ||||
| 
 | ||||
| 	/* per fileystem reservation list head & lock */ | ||||
| 	spin_lock_init(&sbi->s_rsv_window_lock); | ||||
| 	sbi->s_rsv_window_root = RB_ROOT; | ||||
| 	/*
 | ||||
| 	 * Add a single, static dummy reservation to the start of the | ||||
| 	 * reservation window list --- it gives us a placeholder for | ||||
| 	 * append-at-start-of-list which makes the allocation logic | ||||
| 	 * _much_ simpler. | ||||
| 	 */ | ||||
| 	sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; | ||||
| 	sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED; | ||||
| 	sbi->s_rsv_window_head.rsv_alloc_hit = 0; | ||||
| 	sbi->s_rsv_window_head.rsv_goal_size = 0; | ||||
| 	ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); | ||||
| 
 | ||||
| 	err = percpu_counter_init(&sbi->s_freeblocks_counter, | ||||
| 				ext2_count_free_blocks(sb)); | ||||
| 	if (!err) { | ||||
| @ -1260,7 +1293,7 @@ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, | ||||
| 
 | ||||
| 		tmp_bh.b_state = 0; | ||||
| 		err = ext2_get_block(inode, blk, &tmp_bh, 0); | ||||
| 		if (err) | ||||
| 		if (err < 0) | ||||
| 			return err; | ||||
| 		if (!buffer_mapped(&tmp_bh))	/* A hole? */ | ||||
| 			memset(data, 0, tocopy); | ||||
| @ -1299,7 +1332,7 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type, | ||||
| 
 | ||||
| 		tmp_bh.b_state = 0; | ||||
| 		err = ext2_get_block(inode, blk, &tmp_bh, 1); | ||||
| 		if (err) | ||||
| 		if (err < 0) | ||||
| 			goto out; | ||||
| 		if (offset || tocopy != EXT2_BLOCK_SIZE(sb)) | ||||
| 			bh = sb_bread(sb, tmp_bh.b_blocknr); | ||||
|  | ||||
| @ -664,8 +664,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, | ||||
| 						           s_first_data_block) + | ||||
| 				   EXT2_I(inode)->i_block_group * | ||||
| 				   EXT2_BLOCKS_PER_GROUP(sb); | ||||
| 			int block = ext2_new_block(inode, goal, | ||||
| 						   NULL, NULL, &error); | ||||
| 			int block = ext2_new_block(inode, goal, &error); | ||||
| 			if (error) | ||||
| 				goto cleanup; | ||||
| 			ea_idebug(inode, "creating block %d", block); | ||||
|  | ||||
| @ -29,11 +29,12 @@ | ||||
| #undef EXT2FS_DEBUG | ||||
| 
 | ||||
| /*
 | ||||
|  * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files | ||||
|  * Define EXT2_RESERVATION to reserve data blocks for expanding files | ||||
|  */ | ||||
| #define EXT2_PREALLOCATE | ||||
| #define EXT2_DEFAULT_PREALLOC_BLOCKS	8 | ||||
| 
 | ||||
| #define EXT2_DEFAULT_RESERVE_BLOCKS     8 | ||||
| /*max window size: 1024(direct blocks) + 3([t,d]indirect blocks) */ | ||||
| #define EXT2_MAX_RESERVE_BLOCKS         1027 | ||||
| #define EXT2_RESERVE_WINDOW_NOT_ALLOCATED 0 | ||||
| /*
 | ||||
|  * The second extended file system version | ||||
|  */ | ||||
| @ -200,6 +201,8 @@ struct ext2_group_desc | ||||
| #define	EXT2_IOC_SETFLAGS		FS_IOC_SETFLAGS | ||||
| #define	EXT2_IOC_GETVERSION		FS_IOC_GETVERSION | ||||
| #define	EXT2_IOC_SETVERSION		FS_IOC_SETVERSION | ||||
| #define	EXT2_IOC_GETRSVSZ		_IOR('f', 5, long) | ||||
| #define	EXT2_IOC_SETRSVSZ		_IOW('f', 6, long) | ||||
| 
 | ||||
| /*
 | ||||
|  * ioctl commands in 32 bit emulation | ||||
| @ -317,8 +320,9 @@ struct ext2_inode { | ||||
| #define EXT2_MOUNT_XATTR_USER		0x004000  /* Extended user attributes */ | ||||
| #define EXT2_MOUNT_POSIX_ACL		0x008000  /* POSIX Access Control Lists */ | ||||
| #define EXT2_MOUNT_XIP			0x010000  /* Execute in place */ | ||||
| #define EXT2_MOUNT_USRQUOTA		0x020000 /* user quota */ | ||||
| #define EXT2_MOUNT_GRPQUOTA		0x040000 /* group quota */ | ||||
| #define EXT2_MOUNT_USRQUOTA		0x020000  /* user quota */ | ||||
| #define EXT2_MOUNT_GRPQUOTA		0x040000  /* group quota */ | ||||
| #define EXT2_MOUNT_RESERVATION		0x080000  /* Preallocation */ | ||||
| 
 | ||||
| 
 | ||||
| #define clear_opt(o, opt)		o &= ~EXT2_MOUNT_##opt | ||||
| @ -558,4 +562,11 @@ enum { | ||||
| #define EXT2_DIR_REC_LEN(name_len)	(((name_len) + 8 + EXT2_DIR_ROUND) & \ | ||||
| 					 ~EXT2_DIR_ROUND) | ||||
| 
 | ||||
| static inline ext2_fsblk_t | ||||
| ext2_group_first_block_no(struct super_block *sb, unsigned long group_no) | ||||
| { | ||||
| 	return group_no * (ext2_fsblk_t)EXT2_BLOCKS_PER_GROUP(sb) + | ||||
| 		le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block); | ||||
| } | ||||
| 
 | ||||
| #endif	/* _LINUX_EXT2_FS_H */ | ||||
|  | ||||
| @ -18,6 +18,52 @@ | ||||
| 
 | ||||
| #include <linux/blockgroup_lock.h> | ||||
| #include <linux/percpu_counter.h> | ||||
| #include <linux/rbtree.h> | ||||
| 
 | ||||
| /* XXX Here for now... not interested in restructing headers JUST now */ | ||||
| 
 | ||||
| /* data type for block offset of block group */ | ||||
| typedef int ext2_grpblk_t; | ||||
| 
 | ||||
| /* data type for filesystem-wide blocks number */ | ||||
| typedef unsigned long ext2_fsblk_t; | ||||
| 
 | ||||
| #define E2FSBLK "%lu" | ||||
| 
 | ||||
| struct ext2_reserve_window { | ||||
| 	ext2_fsblk_t		_rsv_start;	/* First byte reserved */ | ||||
| 	ext2_fsblk_t		_rsv_end;	/* Last byte reserved or 0 */ | ||||
| }; | ||||
| 
 | ||||
| struct ext2_reserve_window_node { | ||||
| 	struct rb_node	 	rsv_node; | ||||
| 	__u32			rsv_goal_size; | ||||
| 	__u32			rsv_alloc_hit; | ||||
| 	struct ext2_reserve_window	rsv_window; | ||||
| }; | ||||
| 
 | ||||
| struct ext2_block_alloc_info { | ||||
| 	/* information about reservation window */ | ||||
| 	struct ext2_reserve_window_node	rsv_window_node; | ||||
| 	/*
 | ||||
| 	 * was i_next_alloc_block in ext2_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 ext2_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. | ||||
| 	 */ | ||||
| 	ext2_fsblk_t		last_alloc_physical_block; | ||||
| }; | ||||
| 
 | ||||
| #define rsv_start rsv_window._rsv_start | ||||
| #define rsv_end rsv_window._rsv_end | ||||
| 
 | ||||
| /*
 | ||||
|  * second extended-fs super-block data in memory | ||||
| @ -56,6 +102,10 @@ struct ext2_sb_info { | ||||
| 	struct percpu_counter s_freeinodes_counter; | ||||
| 	struct percpu_counter s_dirs_counter; | ||||
| 	struct blockgroup_lock s_blockgroup_lock; | ||||
| 	/* root of the per fs reservation window tree */ | ||||
| 	spinlock_t s_rsv_window_lock; | ||||
| 	struct rb_root s_rsv_window_root; | ||||
| 	struct ext2_reserve_window_node s_rsv_window_head; | ||||
| }; | ||||
| 
 | ||||
| #endif	/* _LINUX_EXT2_FS_SB */ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user