gfs2: Fixes to "Implement iomap for block_map"

"Linux Kernel Mailing List" <[email protected]> Wed, 14 Feb 2018 18:19:53 +0000 (UTC)
Newsgroups gmane.linux.kernel.commits.head
Message-ID <[email protected]>
Web:        https://git.kernel.org/torvalds/c/49edd5bf429c405b3a7f75503845d9f66a47dd4b
Commit:     49edd5bf429c405b3a7f75503845d9f66a47dd4b
Parent:     35277995e17919ab838beae765f440674e8576eb
Refname:    refs/heads/master
Author:     Andreas Gruenbacher <[email protected]>
AuthorDate: Tue Feb 6 07:20:55 2018 -0700
Committer:  Bob Peterson <[email protected]>
CommitDate: Tue Feb 13 13:38:10 2018 -0700

    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 <[email protected]>
    Signed-off-by: Bob Peterson <[email protected]>
---
 fs/gfs2/bmap.c | 43 +++++++++++++++++++++++--------------------
 1 file changed, 23 insertions(+), 20 deletions(-)

diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 86863792f36a..86d6a4435c87 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -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);
 	}