block: take the inode lock for buffered write fallback

Commit c0e473a0d2 ("block: fix race between set_blocksize and read
paths") added shared inode locking around ordinary buffered writes,
but missed the direct-I/O fallback path.

Move the lock into blkdev_buffered_write() so both paths serialize
with set_blocksize().

Also correct the comment claiming that this path takes invalidate_lock;
it only takes i_rwsem.

Fixes: c0e473a0d2 ("block: fix race between set_blocksize and read paths")
Signed-off-by: Julian Sun <sunjunchao@bytedance.com>
Link: https://patch.msgid.link/20261015091105.1315605-3-sunjunchao@bytedance.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Julian Sun 2026-10-15 17:11:05 +08:00 committed by Jens Axboe
parent a08bf4b9f8
commit 1b1be9ea4e

View File

@ -714,12 +714,22 @@ blkdev_direct_write(struct kiocb *iocb, struct iov_iter *from)
static ssize_t blkdev_buffered_write(struct kiocb *iocb, struct iov_iter *from) static ssize_t blkdev_buffered_write(struct kiocb *iocb, struct iov_iter *from)
{ {
struct inode *inode = bdev_file_inode(iocb->ki_filp);
ssize_t ret;
/* Buffered fallback waits for writeback, which NOWAIT cannot allow. */ /* Buffered fallback waits for writeback, which NOWAIT cannot allow. */
if (iocb->ki_flags & IOCB_NOWAIT) if (iocb->ki_flags & IOCB_NOWAIT)
return -EAGAIN; return -EAGAIN;
return iomap_file_buffered_write(iocb, from, &blkdev_iomap_ops, NULL, /*
NULL); * Take i_rwsem to avoid racing with set_blocksize() changing
* i_blkbits/folio order and truncating the page cache.
*/
inode_lock_shared(inode);
ret = iomap_file_buffered_write(iocb, from, &blkdev_iomap_ops, NULL,
NULL);
inode_unlock_shared(inode);
return ret;
} }
/* /*
@ -808,14 +818,7 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
need_sync = true; need_sync = true;
} }
} else { } else {
/*
* Take i_rwsem and invalidate_lock to avoid racing with
* set_blocksize changing i_blkbits/folio order and punching
* out the pagecache.
*/
inode_lock_shared(bd_inode);
ret = blkdev_buffered_write(iocb, from); ret = blkdev_buffered_write(iocb, from);
inode_unlock_shared(bd_inode);
need_sync = true; need_sync = true;
} }