@@ -422,7 +422,8 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, fmode_t mode,
op = REQ_OP_ZONE_RESET;
/* Invalidate the page cache, including dirty pages. */
- filemap_invalidate_lock(bdev->bd_inode->i_mapping);
+ if (filemap_invalidate_lock_killable(bdev->bd_inode->i_mapping))
+ return -EINTR;
ret = blkdev_truncate_zone_range(bdev, mode, &zrange);
if (ret)
goto fail;
@@ -656,7 +656,8 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start,
if ((start | len) & (bdev_logical_block_size(bdev) - 1))
return -EINVAL;
- filemap_invalidate_lock(inode->i_mapping);
+ if (filemap_invalidate_lock_killable(inode->i_mapping))
+ return -EINTR;
/* Invalidate the page cache, including dirty pages. */
error = truncate_bdev_range(bdev, file->f_mode, start, end);
@@ -111,7 +111,8 @@ static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode,
if (start + len > bdev_nr_bytes(bdev))
return -EINVAL;
- filemap_invalidate_lock(inode->i_mapping);
+ if (filemap_invalidate_lock_killable(inode->i_mapping))
+ return -EINTR;
err = truncate_bdev_range(bdev, mode, start, start + len - 1);
if (err)
goto fail;
@@ -152,7 +153,8 @@ static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode,
return -EINVAL;
/* Invalidate the page cache, including dirty pages */
- filemap_invalidate_lock(inode->i_mapping);
+ if (filemap_invalidate_lock_killable(inode->i_mapping))
+ return -EINTR;
err = truncate_bdev_range(bdev, mode, start, end);
if (err)
goto fail;
@@ -859,7 +859,8 @@ static int do_dentry_open(struct file *f,
if (filemap_nr_thps(inode->i_mapping)) {
struct address_space *mapping = inode->i_mapping;
- filemap_invalidate_lock(inode->i_mapping);
+ if (filemap_invalidate_lock_killable(inode->i_mapping))
+ return -EINTR;
/*
* unmap_mapping_range just need to be called once
* here, because the private pages is not need to be
@@ -797,6 +797,11 @@ static inline void filemap_invalidate_lock(struct address_space *mapping)
down_write(&mapping->invalidate_lock);
}
+static inline int filemap_invalidate_lock_killable(struct address_space *mapping)
+{
+ return down_write_killable(&mapping->invalidate_lock);
+}
+
static inline void filemap_invalidate_unlock(struct address_space *mapping)
{
up_write(&mapping->invalidate_lock);
syzbot is reporting hung task at blkdev_fallocate() [1], for it can take minutes with mapping->invalidate_lock held. Since fallocate() has to accept 64bits size, we can't predict how long it will take. Thus, mitigate this problem by using killable wait where possible. ---------- #define _GNU_SOURCE #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char *argv[]) { fork(); fallocate(open("/dev/nullb0", O_RDWR), 0x11, 0, ~0UL >> 1); return 0; } ---------- Note that, even after this patch, e.g. "cat /dev/nullb0" can be reported as hung task at filemap_invalidate_lock_shared() when this reproducer is running. We will need to also make fault-acceptable reads killable. __schedule+0x9a0/0xb20 schedule+0xc1/0x120 rwsem_down_read_slowpath+0x3b5/0x670 __down_read_common+0x56/0x1f0 page_cache_ra_unbounded+0x12d/0x400 filemap_read+0x4bb/0x1280 blkdev_read_iter+0x1d5/0x260 vfs_read+0x5f8/0x690 ksys_read+0xee/0x190 do_syscall_64+0x3d/0x90 entry_SYSCALL_64_after_hwframe+0x44/0xae Link: https://syzkaller.appspot.com/bug?extid=39b75c02b8be0a061bfc [1] Reported-by: syzbot <syzbot+39b75c02b8be0a061bfc@syzkaller.appspotmail.com> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> --- Changes in v2: Converted all users in block/ directory and fs/open.c file. I didn't convert remaining users because remaining users should be carefully converted by each filesystem's developers. block/blk-zoned.c | 3 ++- block/fops.c | 3 ++- block/ioctl.c | 6 ++++-- fs/open.c | 3 ++- include/linux/fs.h | 5 +++++ 5 files changed, 15 insertions(+), 5 deletions(-)