scoutfs: limit get_block bh use

We were seeing __block_write_begin spin when staging writes were called
after a read of an offline region saw an error.  It turns out that
the way __block_write_begin iterates through buffer heads on a page will
livelock if b_size is 0.

Our get_block was clearing b_blocknr and b_size before doing anything.
It'd set them when it allocated blocks or found existing mapped
blocks.  But it'd leave them 0 on an error and trigger this hang.

So we'll back off and only do the same things to the result bh that
ext2/3 do, presumably that's what's actually supported.  We only set
mapped, set or clear new, and set b_size to less than the input b_size.

While we're at it we remove a totally bogus extent flag check that's
done before seeing if the next extent we found even intersects with the
logical block that we're searching for.  The extra test is performed
again correctly inside the check for the extents overlapping.  It is an
artifact from the days when the "extents" were a single block and didn't
need to check for overlaps.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2017-09-07 13:49:37 -07:00
committed by Mark Fasheh
parent 51e8b614e5
commit e7b5cd4c66
+2 -9
View File
@@ -995,9 +995,6 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock,
u64 off;
int ret;
bh->b_blocknr = 0;
bh->b_size = 0;
ext.blk_off = iblock;
ext.blocks = 1;
ext.blkno = 0;
@@ -1026,11 +1023,6 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock,
trace_printk("found nei "EXTF"\n", EXTA(&ext));
}
if ((ext.flags & SCOUTFS_FILE_EXTENT_OFFLINE) && !si->staging) {
ret = -EINVAL;
goto out;
}
/* use the extent if it intersects */
if (iblock >= ext.blk_off && iblock < (ext.blk_off + ext.blocks)) {
@@ -1045,8 +1037,9 @@ static int scoutfs_get_block(struct inode *inode, sector_t iblock,
/* found online extent */
off = iblock - ext.blk_off;
map_bh(bh, inode->i_sb, ext.blkno + off);
bh->b_size = min_t(u64, SIZE_MAX,
bh->b_size = min_t(u64, bh->b_size,
(ext.blocks - off) << SCOUTFS_BLOCK_SHIFT);
clear_buffer_new(bh);
}
}