From e7b5cd4c66c4b3f1aec8cacd4360da75f2b141f8 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 5 Sep 2017 16:14:31 -0700 Subject: [PATCH] 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 --- kmod/src/data.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/kmod/src/data.c b/kmod/src/data.c index b6f2d641..aff8d7fe 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -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); } }