mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +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_add(struct btrfs_trans_handle *trans, struct inode *inode); | ||||||
| int btrfs_orphan_del(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); | void btrfs_orphan_cleanup(struct btrfs_root *root); | ||||||
|  | int btrfs_cont_expand(struct inode *inode, loff_t size); | ||||||
| 
 | 
 | ||||||
| /* ioctl.c */ | /* ioctl.c */ | ||||||
| long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); | long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| /* bits for the flags field */ | /* bits for the flags field */ | ||||||
| #define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */ | #define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */ | ||||||
| #define EXTENT_FLAG_COMPRESSED 1 | #define EXTENT_FLAG_COMPRESSED 1 | ||||||
|  | #define EXTENT_FLAG_VACANCY 2 /* no file extent item found */ | ||||||
| 
 | 
 | ||||||
| struct extent_map { | struct extent_map { | ||||||
| 	struct rb_node rb_node; | 	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); | 	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
 | 	/* check for reserved extents on each page, we don't want
 | ||||||
| 	 * to reset the delalloc bit on things that already have | 	 * to reset the delalloc bit on things that already have | ||||||
| 	 * extents reserved. | 	 * extents reserved. | ||||||
|  | @ -191,7 +157,6 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans, | ||||||
| 		i_size_write(inode, end_pos); | 		i_size_write(inode, end_pos); | ||||||
| 		btrfs_update_inode(trans, root, inode); | 		btrfs_update_inode(trans, root, inode); | ||||||
| 	} | 	} | ||||||
| failed: |  | ||||||
| 	err = btrfs_end_transaction(trans, root); | 	err = btrfs_end_transaction(trans, root); | ||||||
| out_unlock: | out_unlock: | ||||||
| 	unlock_extent(io_tree, start_pos, end_of_last_block, GFP_NOFS); | 	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); | 	start_pos = pos & ~((u64)root->sectorsize - 1); | ||||||
| 	last_pos = ((u64)index + num_pages) << PAGE_CACHE_SHIFT; | 	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 *)); | 	memset(pages, 0, num_pages * sizeof(struct page *)); | ||||||
| again: | again: | ||||||
| 	for (i = 0; i < num_pages; i++) { | 	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; | 	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) | static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) | ||||||
| { | { | ||||||
| 	struct inode *inode = dentry->d_inode; | 	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) && | 	if (S_ISREG(inode->i_mode) && | ||||||
| 	    attr->ia_valid & ATTR_SIZE && attr->ia_size > inode->i_size) { | 	    attr->ia_valid & ATTR_SIZE && attr->ia_size > inode->i_size) { | ||||||
| 		struct btrfs_trans_handle *trans; | 		err = btrfs_cont_expand(inode, attr->ia_size); | ||||||
| 		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); |  | ||||||
| 		if (err) | 		if (err) | ||||||
| 			return err; | 			return err; | ||||||
| 	} | 	} | ||||||
| out: | 
 | ||||||
| 	err = inode_setattr(inode, attr); | 	err = inode_setattr(inode, attr); | ||||||
| 
 | 
 | ||||||
| 	if (!err && ((attr->ia_valid & ATTR_MODE))) | 	if (!err && ((attr->ia_valid & ATTR_MODE))) | ||||||
| 		err = btrfs_acl_chmod(inode); | 		err = btrfs_acl_chmod(inode); | ||||||
| fail: |  | ||||||
| 	return err; | 	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) { | 	if (found_type == BTRFS_FILE_EXTENT_REG) { | ||||||
| 		extent_end = extent_start + | 		extent_end = extent_start + | ||||||
| 		       btrfs_file_extent_num_bytes(leaf, item); | 		       btrfs_file_extent_num_bytes(leaf, item); | ||||||
| 		err = 0; | 	} else if (found_type == BTRFS_FILE_EXTENT_INLINE) { | ||||||
| 		if (start < extent_start || start >= extent_end) { | 		size_t size; | ||||||
| 			em->start = start; | 		size = btrfs_file_extent_inline_len(leaf, item); | ||||||
| 			if (start < extent_start) { | 		extent_end = (extent_start + size + root->sectorsize - 1) & | ||||||
| 				if (start + len <= extent_start) | 			~((u64)root->sectorsize - 1); | ||||||
| 					goto not_found; | 	} | ||||||
| 				em->len = extent_end - extent_start; | 
 | ||||||
| 			} else { | 	if (start >= extent_end) { | ||||||
| 				em->len = len; | 		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); | 		bytenr = btrfs_file_extent_disk_bytenr(leaf, item); | ||||||
| 		if (bytenr == 0) { | 		if (bytenr == 0) { | ||||||
| 			em->start = extent_start; |  | ||||||
| 			em->len = extent_end - extent_start; |  | ||||||
| 			em->block_start = EXTENT_MAP_HOLE; | 			em->block_start = EXTENT_MAP_HOLE; | ||||||
| 			goto insert; | 			goto insert; | ||||||
| 		} | 		} | ||||||
| 		em->start = extent_start; |  | ||||||
| 		em->len = extent_end - extent_start; |  | ||||||
| 		if (compressed) { | 		if (compressed) { | ||||||
| 			set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); | 			set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); | ||||||
| 			em->block_start = bytenr; | 			em->block_start = bytenr; | ||||||
|  | @ -3489,38 +3516,21 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, | ||||||
| 		} | 		} | ||||||
| 		goto insert; | 		goto insert; | ||||||
| 	} else if (found_type == BTRFS_FILE_EXTENT_INLINE) { | 	} else if (found_type == BTRFS_FILE_EXTENT_INLINE) { | ||||||
| 		u64 page_start; |  | ||||||
| 		unsigned long ptr; | 		unsigned long ptr; | ||||||
| 		char *map; | 		char *map; | ||||||
| 		size_t size; | 		size_t size; | ||||||
| 		size_t extent_offset; | 		size_t extent_offset; | ||||||
| 		size_t copy_size; | 		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; | 		em->block_start = EXTENT_MAP_INLINE; | ||||||
| 
 |  | ||||||
| 		if (!page || create) { | 		if (!page || create) { | ||||||
| 			em->start = extent_start; | 			em->start = extent_start; | ||||||
| 			em->len = (size + root->sectorsize - 1) & | 			em->len = extent_end - extent_start; | ||||||
| 			~((u64)root->sectorsize - 1); |  | ||||||
| 			goto out; | 			goto out; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		page_start = page_offset(page) + pg_offset; | 		size = btrfs_file_extent_inline_len(leaf, item); | ||||||
| 		extent_offset = page_start - extent_start; | 		extent_offset = page_offset(page) + pg_offset - extent_start; | ||||||
| 		copy_size = min_t(u64, PAGE_CACHE_SIZE - pg_offset, | 		copy_size = min_t(u64, PAGE_CACHE_SIZE - pg_offset, | ||||||
| 				size - extent_offset); | 				size - extent_offset); | ||||||
| 		em->start = extent_start + 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; | 	em->len = len; | ||||||
| not_found_em: | not_found_em: | ||||||
| 	em->block_start = EXTENT_MAP_HOLE; | 	em->block_start = EXTENT_MAP_HOLE; | ||||||
|  | 	set_bit(EXTENT_FLAG_VACANCY, &em->flags); | ||||||
| insert: | insert: | ||||||
| 	btrfs_release_path(root, path); | 	btrfs_release_path(root, path); | ||||||
| 	if (em->start > start || extent_map_end(em) <= start) { | 	if (em->start > start || extent_map_end(em) <= start) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Yan Zheng
						Yan Zheng