mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 00:28:52 +02:00 
			
		
		
		
	Btrfs: update hole handling v2
This patch splits the hole insertion code out of btrfs_setattr into btrfs_cont_expand and updates btrfs_get_extent to properly handle the case that file extent items are not continuous. Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
This commit is contained in:
		
							parent
							
								
									19b9bdb054
								
							
						
					
					
						commit
						9036c10208
					
				
					 4 changed files with 110 additions and 126 deletions
				
			
		|  | @ -1908,6 +1908,7 @@ int btrfs_update_inode(struct btrfs_trans_handle *trans, | |||
| int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode); | ||||
| int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode); | ||||
| void btrfs_orphan_cleanup(struct btrfs_root *root); | ||||
| int btrfs_cont_expand(struct inode *inode, loff_t size); | ||||
| 
 | ||||
| /* ioctl.c */ | ||||
| long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ | |||
| /* bits for the flags field */ | ||||
| #define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */ | ||||
| #define EXTENT_FLAG_COMPRESSED 1 | ||||
| #define EXTENT_FLAG_VACANCY 2 /* no file extent item found */ | ||||
| 
 | ||||
| struct extent_map { | ||||
| 	struct rb_node rb_node; | ||||
|  |  | |||
|  | @ -142,40 +142,6 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans, | |||
| 	} | ||||
| 	set_extent_uptodate(io_tree, start_pos, end_of_last_block, GFP_NOFS); | ||||
| 
 | ||||
| 	/* FIXME...EIEIO, ENOSPC and more */ | ||||
| 	/* insert any holes we need to create */ | ||||
| 	if (isize < start_pos) { | ||||
| 		u64 last_pos_in_file; | ||||
| 		u64 hole_size; | ||||
| 		u64 mask = root->sectorsize - 1; | ||||
| 		last_pos_in_file = (isize + mask) & ~mask; | ||||
| 		hole_size = (start_pos - last_pos_in_file + mask) & ~mask; | ||||
| 		if (hole_size > 0) { | ||||
| 			btrfs_wait_ordered_range(inode, last_pos_in_file, | ||||
| 						 last_pos_in_file + hole_size); | ||||
| 			mutex_lock(&BTRFS_I(inode)->extent_mutex); | ||||
| 			err = btrfs_drop_extents(trans, root, inode, | ||||
| 						 last_pos_in_file, | ||||
| 						 last_pos_in_file + hole_size, | ||||
| 						 last_pos_in_file, | ||||
| 						 &hint_byte); | ||||
| 			if (err) | ||||
| 				goto failed; | ||||
| 
 | ||||
| 			err = btrfs_insert_file_extent(trans, root, | ||||
| 						       inode->i_ino, | ||||
| 						       last_pos_in_file, | ||||
| 						       0, 0, hole_size, 0, | ||||
| 						       hole_size, 0, 0, 0); | ||||
| 			btrfs_drop_extent_cache(inode, last_pos_in_file, | ||||
| 					last_pos_in_file + hole_size - 1, 0); | ||||
| 			mutex_unlock(&BTRFS_I(inode)->extent_mutex); | ||||
| 			btrfs_check_file(root, inode); | ||||
| 		} | ||||
| 		if (err) | ||||
| 			goto failed; | ||||
| 	} | ||||
| 
 | ||||
| 	/* check for reserved extents on each page, we don't want
 | ||||
| 	 * to reset the delalloc bit on things that already have | ||||
| 	 * extents reserved. | ||||
|  | @ -191,7 +157,6 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans, | |||
| 		i_size_write(inode, end_pos); | ||||
| 		btrfs_update_inode(trans, root, inode); | ||||
| 	} | ||||
| failed: | ||||
| 	err = btrfs_end_transaction(trans, root); | ||||
| out_unlock: | ||||
| 	unlock_extent(io_tree, start_pos, end_of_last_block, GFP_NOFS); | ||||
|  | @ -697,6 +662,12 @@ static int noinline prepare_pages(struct btrfs_root *root, struct file *file, | |||
| 	start_pos = pos & ~((u64)root->sectorsize - 1); | ||||
| 	last_pos = ((u64)index + num_pages) << PAGE_CACHE_SHIFT; | ||||
| 
 | ||||
| 	if (start_pos > inode->i_size) { | ||||
| 		err = btrfs_cont_expand(inode, start_pos); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 
 | ||||
| 	memset(pages, 0, num_pages * sizeof(struct page *)); | ||||
| again: | ||||
| 	for (i = 0; i < num_pages; i++) { | ||||
|  |  | |||
							
								
								
									
										193
									
								
								fs/btrfs/inode.c
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								fs/btrfs/inode.c
									
									
									
									
									
								
							|  | @ -2296,6 +2296,71 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from) | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int btrfs_cont_expand(struct inode *inode, loff_t size) | ||||
| { | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; | ||||
| 	struct extent_map *em; | ||||
| 	u64 mask = root->sectorsize - 1; | ||||
| 	u64 hole_start = (inode->i_size + mask) & ~mask; | ||||
| 	u64 block_end = (size + mask) & ~mask; | ||||
| 	u64 last_byte; | ||||
| 	u64 cur_offset; | ||||
| 	u64 hole_size; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (size <= hole_start) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	err = btrfs_check_free_space(root, 1, 0); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	btrfs_truncate_page(inode->i_mapping, inode->i_size); | ||||
| 
 | ||||
| 	while (1) { | ||||
| 		struct btrfs_ordered_extent *ordered; | ||||
| 		btrfs_wait_ordered_range(inode, hole_start, | ||||
| 					 block_end - hole_start); | ||||
| 		lock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS); | ||||
| 		ordered = btrfs_lookup_ordered_extent(inode, hole_start); | ||||
| 		if (!ordered) | ||||
| 			break; | ||||
| 		unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS); | ||||
| 		btrfs_put_ordered_extent(ordered); | ||||
| 	} | ||||
| 
 | ||||
| 	trans = btrfs_start_transaction(root, 1); | ||||
| 	btrfs_set_trans_block_group(trans, inode); | ||||
| 
 | ||||
| 	cur_offset = hole_start; | ||||
| 	while (1) { | ||||
| 		em = btrfs_get_extent(inode, NULL, 0, cur_offset, | ||||
| 				block_end - cur_offset, 0); | ||||
| 		BUG_ON(IS_ERR(em) || !em); | ||||
| 		last_byte = min(extent_map_end(em), block_end); | ||||
| 		last_byte = (last_byte + mask) & ~mask; | ||||
| 		if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) { | ||||
| 			hole_size = last_byte - cur_offset; | ||||
| 			err = btrfs_insert_file_extent(trans, root, | ||||
| 					inode->i_ino, cur_offset, 0, | ||||
| 					0, hole_size, 0, hole_size, | ||||
| 					0, 0, 0); | ||||
| 			btrfs_drop_extent_cache(inode, hole_start, | ||||
| 					last_byte - 1, 0); | ||||
| 		} | ||||
| 		free_extent_map(em); | ||||
| 		cur_offset = last_byte; | ||||
| 		if (err || cur_offset >= block_end) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	btrfs_end_transaction(trans, root); | ||||
| 	unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) | ||||
| { | ||||
| 	struct inode *inode = dentry->d_inode; | ||||
|  | @ -2307,70 +2372,15 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) | |||
| 
 | ||||
| 	if (S_ISREG(inode->i_mode) && | ||||
| 	    attr->ia_valid & ATTR_SIZE && attr->ia_size > inode->i_size) { | ||||
| 		struct btrfs_trans_handle *trans; | ||||
| 		struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 		struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; | ||||
| 
 | ||||
| 		u64 mask = root->sectorsize - 1; | ||||
| 		u64 hole_start = (inode->i_size + mask) & ~mask; | ||||
| 		u64 block_end = (attr->ia_size + mask) & ~mask; | ||||
| 		u64 hole_size; | ||||
| 		u64 alloc_hint = 0; | ||||
| 
 | ||||
| 		if (attr->ia_size <= hole_start) | ||||
| 			goto out; | ||||
| 
 | ||||
| 		err = btrfs_check_free_space(root, 1, 0); | ||||
| 		if (err) | ||||
| 			goto fail; | ||||
| 
 | ||||
| 		btrfs_truncate_page(inode->i_mapping, inode->i_size); | ||||
| 
 | ||||
| 		hole_size = block_end - hole_start; | ||||
| 		while(1) { | ||||
| 			struct btrfs_ordered_extent *ordered; | ||||
| 			btrfs_wait_ordered_range(inode, hole_start, hole_size); | ||||
| 
 | ||||
| 			lock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS); | ||||
| 			ordered = btrfs_lookup_ordered_extent(inode, hole_start); | ||||
| 			if (ordered) { | ||||
| 				unlock_extent(io_tree, hole_start, | ||||
| 					      block_end - 1, GFP_NOFS); | ||||
| 				btrfs_put_ordered_extent(ordered); | ||||
| 			} else { | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		trans = btrfs_start_transaction(root, 1); | ||||
| 		btrfs_set_trans_block_group(trans, inode); | ||||
| 		mutex_lock(&BTRFS_I(inode)->extent_mutex); | ||||
| 		err = btrfs_drop_extents(trans, root, inode, | ||||
| 					 hole_start, block_end, hole_start, | ||||
| 					 &alloc_hint); | ||||
| 
 | ||||
| 		if (alloc_hint != EXTENT_MAP_INLINE) { | ||||
| 			err = btrfs_insert_file_extent(trans, root, | ||||
| 						       inode->i_ino, | ||||
| 						       hole_start, 0, 0, | ||||
| 						       hole_size, 0, hole_size, | ||||
| 						       0, 0, 0); | ||||
| 			btrfs_drop_extent_cache(inode, hole_start, | ||||
| 						(u64)-1, 0); | ||||
| 			btrfs_check_file(root, inode); | ||||
| 		} | ||||
| 		mutex_unlock(&BTRFS_I(inode)->extent_mutex); | ||||
| 		btrfs_end_transaction(trans, root); | ||||
| 		unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS); | ||||
| 		err = btrfs_cont_expand(inode, attr->ia_size); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| out: | ||||
| 
 | ||||
| 	err = inode_setattr(inode, attr); | ||||
| 
 | ||||
| 	if (!err && ((attr->ia_valid & ATTR_MODE))) | ||||
| 		err = btrfs_acl_chmod(inode); | ||||
| fail: | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
|  | @ -3456,27 +3466,44 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, | |||
| 	if (found_type == BTRFS_FILE_EXTENT_REG) { | ||||
| 		extent_end = extent_start + | ||||
| 		       btrfs_file_extent_num_bytes(leaf, item); | ||||
| 		err = 0; | ||||
| 		if (start < extent_start || start >= extent_end) { | ||||
| 			em->start = start; | ||||
| 			if (start < extent_start) { | ||||
| 				if (start + len <= extent_start) | ||||
| 					goto not_found; | ||||
| 				em->len = extent_end - extent_start; | ||||
| 			} else { | ||||
| 				em->len = len; | ||||
| 	} else if (found_type == BTRFS_FILE_EXTENT_INLINE) { | ||||
| 		size_t size; | ||||
| 		size = btrfs_file_extent_inline_len(leaf, item); | ||||
| 		extent_end = (extent_start + size + root->sectorsize - 1) & | ||||
| 			~((u64)root->sectorsize - 1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (start >= extent_end) { | ||||
| 		path->slots[0]++; | ||||
| 		if (path->slots[0] >= btrfs_header_nritems(leaf)) { | ||||
| 			ret = btrfs_next_leaf(root, path); | ||||
| 			if (ret < 0) { | ||||
| 				err = ret; | ||||
| 				goto out; | ||||
| 			} | ||||
| 			goto not_found_em; | ||||
| 			if (ret > 0) | ||||
| 				goto not_found; | ||||
| 			leaf = path->nodes[0]; | ||||
| 		} | ||||
| 		btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); | ||||
| 		if (found_key.objectid != objectid || | ||||
| 		    found_key.type != BTRFS_EXTENT_DATA_KEY) | ||||
| 			goto not_found; | ||||
| 		if (start + len <= found_key.offset) | ||||
| 			goto not_found; | ||||
| 		em->start = start; | ||||
| 		em->len = found_key.offset - start; | ||||
| 		goto not_found_em; | ||||
| 	} | ||||
| 
 | ||||
| 	if (found_type == BTRFS_FILE_EXTENT_REG) { | ||||
| 		em->start = extent_start; | ||||
| 		em->len = extent_end - extent_start; | ||||
| 		bytenr = btrfs_file_extent_disk_bytenr(leaf, item); | ||||
| 		if (bytenr == 0) { | ||||
| 			em->start = extent_start; | ||||
| 			em->len = extent_end - extent_start; | ||||
| 			em->block_start = EXTENT_MAP_HOLE; | ||||
| 			goto insert; | ||||
| 		} | ||||
| 		em->start = extent_start; | ||||
| 		em->len = extent_end - extent_start; | ||||
| 		if (compressed) { | ||||
| 			set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); | ||||
| 			em->block_start = bytenr; | ||||
|  | @ -3489,38 +3516,21 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, | |||
| 		} | ||||
| 		goto insert; | ||||
| 	} else if (found_type == BTRFS_FILE_EXTENT_INLINE) { | ||||
| 		u64 page_start; | ||||
| 		unsigned long ptr; | ||||
| 		char *map; | ||||
| 		size_t size; | ||||
| 		size_t extent_offset; | ||||
| 		size_t copy_size; | ||||
| 
 | ||||
| 		size = btrfs_file_extent_inline_len(leaf, item); | ||||
| 		extent_end = (extent_start + size + root->sectorsize - 1) & | ||||
| 			~((u64)root->sectorsize - 1); | ||||
| 		if (start < extent_start || start >= extent_end) { | ||||
| 			em->start = start; | ||||
| 			if (start < extent_start) { | ||||
| 				if (start + len <= extent_start) | ||||
| 					goto not_found; | ||||
| 				em->len = extent_end - extent_start; | ||||
| 			} else { | ||||
| 				em->len = len; | ||||
| 			} | ||||
| 			goto not_found_em; | ||||
| 		} | ||||
| 		em->block_start = EXTENT_MAP_INLINE; | ||||
| 
 | ||||
| 		if (!page || create) { | ||||
| 			em->start = extent_start; | ||||
| 			em->len = (size + root->sectorsize - 1) & | ||||
| 			~((u64)root->sectorsize - 1); | ||||
| 			em->len = extent_end - extent_start; | ||||
| 			goto out; | ||||
| 		} | ||||
| 
 | ||||
| 		page_start = page_offset(page) + pg_offset; | ||||
| 		extent_offset = page_start - extent_start; | ||||
| 		size = btrfs_file_extent_inline_len(leaf, item); | ||||
| 		extent_offset = page_offset(page) + pg_offset - extent_start; | ||||
| 		copy_size = min_t(u64, PAGE_CACHE_SIZE - pg_offset, | ||||
| 				size - extent_offset); | ||||
| 		em->start = extent_start + extent_offset; | ||||
|  | @ -3570,6 +3580,7 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, | |||
| 	em->len = len; | ||||
| not_found_em: | ||||
| 	em->block_start = EXTENT_MAP_HOLE; | ||||
| 	set_bit(EXTENT_FLAG_VACANCY, &em->flags); | ||||
| insert: | ||||
| 	btrfs_release_path(root, path); | ||||
| 	if (em->start > start || extent_map_end(em) <= start) { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Yan Zheng
						Yan Zheng