Skip to content

Commit 2e60a51

Browse files
Miao XieJosef Bacik
authored andcommitted
Btrfs: serialize unlocked dio reads with truncate
Currently, we can do unlocked dio reads, but the following race is possible: dio_read_task truncate_task ->btrfs_setattr() ->btrfs_direct_IO ->__blockdev_direct_IO ->btrfs_get_block ->btrfs_truncate() #alloc truncated blocks #to other inode ->submit_io() #INFORMATION LEAK In order to avoid this problem, we must serialize unlocked dio reads with truncate. There are two approaches: - use extent lock to protect the extent that we truncate - use inode_dio_wait() to make sure the truncating task will wait for the read DIO. If we use the 1st one, we will meet the endless truncation problem due to the nonlocked read DIO after we implement the nonlocked write DIO. It is because we still need invoke inode_dio_wait() avoid the race between write DIO and truncation. By that time, we have to introduce btrfs_inode_{block, resume}_nolock_dio() again. That is we have to implement this patch again, so I choose the 2nd way to fix the problem. Signed-off-by: Miao Xie <[email protected]> Signed-off-by: Josef Bacik <[email protected]>
1 parent 0934856 commit 2e60a51

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

fs/btrfs/btrfs_inode.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#define BTRFS_INODE_NEEDS_FULL_SYNC 7
4242
#define BTRFS_INODE_COPY_EVERYTHING 8
4343
#define BTRFS_INODE_IN_DELALLOC_LIST 9
44+
#define BTRFS_INODE_READDIO_NEED_LOCK 10
4445

4546
/* in memory btrfs inode */
4647
struct btrfs_inode {
@@ -217,4 +218,22 @@ static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
217218
return 0;
218219
}
219220

221+
/*
222+
* Disable DIO read nolock optimization, so new dio readers will be forced
223+
* to grab i_mutex. It is used to avoid the endless truncate due to
224+
* nonlocked dio read.
225+
*/
226+
static inline void btrfs_inode_block_unlocked_dio(struct inode *inode)
227+
{
228+
set_bit(BTRFS_INODE_READDIO_NEED_LOCK, &BTRFS_I(inode)->runtime_flags);
229+
smp_mb();
230+
}
231+
232+
static inline void btrfs_inode_resume_unlocked_dio(struct inode *inode)
233+
{
234+
smp_mb__before_clear_bit();
235+
clear_bit(BTRFS_INODE_READDIO_NEED_LOCK,
236+
&BTRFS_I(inode)->runtime_flags);
237+
}
238+
220239
#endif

fs/btrfs/inode.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3888,6 +3888,12 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr)
38883888

38893889
/* we don't support swapfiles, so vmtruncate shouldn't fail */
38903890
truncate_setsize(inode, newsize);
3891+
3892+
/* Disable nonlocked read DIO to avoid the end less truncate */
3893+
btrfs_inode_block_unlocked_dio(inode);
3894+
inode_dio_wait(inode);
3895+
btrfs_inode_resume_unlocked_dio(inode);
3896+
38913897
ret = btrfs_truncate(inode);
38923898
if (ret && inode->i_nlink)
38933899
btrfs_orphan_del(NULL, inode);
@@ -6670,6 +6676,8 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
66706676
struct file *file = iocb->ki_filp;
66716677
struct inode *inode = file->f_mapping->host;
66726678
size_t count = 0;
6679+
int flags = 0;
6680+
bool wakeup = false;
66736681
ssize_t ret;
66746682

66756683
if (check_direct_IO(BTRFS_I(inode)->root, rw, iocb, iov,
@@ -6681,13 +6689,22 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
66816689
ret = btrfs_delalloc_reserve_space(inode, count);
66826690
if (ret)
66836691
return ret;
6692+
} else {
6693+
atomic_inc(&inode->i_dio_count);
6694+
smp_mb__after_atomic_inc();
6695+
if (unlikely(test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
6696+
&BTRFS_I(inode)->runtime_flags))) {
6697+
inode_dio_done(inode);
6698+
flags = DIO_LOCKING | DIO_SKIP_HOLES;
6699+
} else {
6700+
wakeup = true;
6701+
}
66846702
}
66856703

66866704
ret = __blockdev_direct_IO(rw, iocb, inode,
66876705
BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev,
66886706
iov, offset, nr_segs, btrfs_get_blocks_direct, NULL,
6689-
btrfs_submit_direct, 0);
6690-
6707+
btrfs_submit_direct, flags);
66916708
if (rw & WRITE) {
66926709
if (ret < 0 && ret != -EIOCBQUEUED)
66936710
btrfs_delalloc_release_space(inode, count);
@@ -6700,6 +6717,8 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
67006717
}
67016718
btrfs_delalloc_release_metadata(inode, 0);
67026719
}
6720+
if (wakeup)
6721+
inode_dio_done(inode);
67036722

67046723
return ret;
67056724
}

0 commit comments

Comments
 (0)