gfs2: Fixes to "Implement iomap for block_map"
It turns out that commit 3974320ca6 "Implement iomap for block_map"
introduced a few bugs that trigger occasional failures with xfstest
generic/476:
In gfs2_iomap_begin, we jump to do_alloc when we determine that we are
beyond the end of the allocated metadata (height > ip->i_height).
There, we can end up calling hole_size with a metapath that doesn't
match the current metadata tree, which doesn't make sense.  After
untangling the code at do_alloc, fix this by checking if the block we
are looking for is within the range of allocated metadata.
In addition, add a BUG() in case gfs2_iomap_begin is accidentally called
for reading stuffed files: this is handled separately.  Make sure we
don't truncate iomap->length for reads beyond the end of the file; in
that case, the entire range counts as a hole.
Finally, revert to taking a bitmap write lock when doing allocations.
It's unclear why that change didn't lead to any failures during testing.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									35277995e1
								
							
						
					
					
						commit
						49edd5bf42
					
				| @ -716,7 +716,7 @@ int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, | ||||
| 	__be64 *ptr; | ||||
| 	sector_t lblock; | ||||
| 	sector_t lend; | ||||
| 	int ret; | ||||
| 	int ret = 0; | ||||
| 	int eob; | ||||
| 	unsigned int len; | ||||
| 	struct buffer_head *bh; | ||||
| @ -728,12 +728,14 @@ int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if ((flags & IOMAP_REPORT) && gfs2_is_stuffed(ip)) { | ||||
| 		gfs2_stuffed_iomap(inode, iomap); | ||||
| 		if (pos >= iomap->length) | ||||
| 			return -ENOENT; | ||||
| 		ret = 0; | ||||
| 		goto out; | ||||
| 	if (gfs2_is_stuffed(ip)) { | ||||
| 		if (flags & IOMAP_REPORT) { | ||||
| 			gfs2_stuffed_iomap(inode, iomap); | ||||
| 			if (pos >= iomap->length) | ||||
| 				ret = -ENOENT; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		BUG_ON(!(flags & IOMAP_WRITE)); | ||||
| 	} | ||||
| 
 | ||||
| 	lblock = pos >> inode->i_blkbits; | ||||
| @ -744,7 +746,7 @@ int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, | ||||
| 	iomap->type = IOMAP_HOLE; | ||||
| 	iomap->length = (u64)(lend - lblock) << inode->i_blkbits; | ||||
| 	iomap->flags = IOMAP_F_MERGED; | ||||
| 	bmap_lock(ip, 0); | ||||
| 	bmap_lock(ip, flags & IOMAP_WRITE); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Directory data blocks have a struct gfs2_meta_header header, so the | ||||
| @ -787,27 +789,28 @@ int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, | ||||
| 		iomap->flags |= IOMAP_F_BOUNDARY; | ||||
| 	iomap->length = (u64)len << inode->i_blkbits; | ||||
| 
 | ||||
| 	ret = 0; | ||||
| 
 | ||||
| out_release: | ||||
| 	release_metapath(&mp); | ||||
| 	bmap_unlock(ip, 0); | ||||
| 	bmap_unlock(ip, flags & IOMAP_WRITE); | ||||
| out: | ||||
| 	trace_gfs2_iomap_end(ip, iomap, ret); | ||||
| 	return ret; | ||||
| 
 | ||||
| do_alloc: | ||||
| 	if (!(flags & IOMAP_WRITE)) { | ||||
| 		if (pos >= i_size_read(inode)) { | ||||
| 	if (flags & IOMAP_WRITE) { | ||||
| 		ret = gfs2_iomap_alloc(inode, iomap, flags, &mp); | ||||
| 	} else if (flags & IOMAP_REPORT) { | ||||
| 		loff_t size = i_size_read(inode); | ||||
| 		if (pos >= size) | ||||
| 			ret = -ENOENT; | ||||
| 			goto out_release; | ||||
| 		} | ||||
| 		ret = 0; | ||||
| 		iomap->length = hole_size(inode, lblock, &mp); | ||||
| 		goto out_release; | ||||
| 		else if (height <= ip->i_height) | ||||
| 			iomap->length = hole_size(inode, lblock, &mp); | ||||
| 		else | ||||
| 			iomap->length = size - pos; | ||||
| 	} else { | ||||
| 		if (height <= ip->i_height) | ||||
| 			iomap->length = hole_size(inode, lblock, &mp); | ||||
| 	} | ||||
| 
 | ||||
| 	ret = gfs2_iomap_alloc(inode, iomap, flags, &mp); | ||||
| 	goto out_release; | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user