f2fs: add nowait aio support
This patch adds nowait aio support[1]. Return EAGAIN if any of the following checks fail for direct I/O: - i_rwsem is not lockable - Blocks are not allocated at the write location And xfstests generic/471 is passed. [1]: 6be96d "Introduce RWF_NOWAIT and FMODE_AIO_NOWAIT" Signed-off-by: Hyunchul Lee <cheol.lee@lge.com> Reviewed-by: Goldwyn Rodrigues <rgoldwyn@suse.com> Reviewed-by: Chao Yu <yuchao0@huawei.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
d909e94106
commit
30654507e0
4 changed files with 78 additions and 16 deletions
|
@ -837,13 +837,6 @@ alloc:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline bool __force_buffered_io(struct inode *inode, int rw)
|
||||
{
|
||||
return (f2fs_encrypted_file(inode) ||
|
||||
(rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) ||
|
||||
F2FS_I_SB(inode)->s_ndevs);
|
||||
}
|
||||
|
||||
int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
|
@ -875,7 +868,7 @@ int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from)
|
|||
|
||||
if (direct_io) {
|
||||
map.m_seg_type = rw_hint_to_seg_type(iocb->ki_hint);
|
||||
flag = __force_buffered_io(inode, WRITE) ?
|
||||
flag = f2fs_force_buffered_io(inode, WRITE) ?
|
||||
F2FS_GET_BLOCK_PRE_AIO :
|
||||
F2FS_GET_BLOCK_PRE_DIO;
|
||||
goto map_blocks;
|
||||
|
@ -1119,6 +1112,31 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len)
|
||||
{
|
||||
struct f2fs_map_blocks map;
|
||||
block_t last_lblk;
|
||||
int err;
|
||||
|
||||
if (pos + len > i_size_read(inode))
|
||||
return false;
|
||||
|
||||
map.m_lblk = F2FS_BYTES_TO_BLK(pos);
|
||||
map.m_next_pgofs = NULL;
|
||||
map.m_next_extent = NULL;
|
||||
map.m_seg_type = NO_CHECK_TYPE;
|
||||
last_lblk = F2FS_BLK_ALIGN(pos + len);
|
||||
|
||||
while (map.m_lblk < last_lblk) {
|
||||
map.m_len = last_lblk - map.m_lblk;
|
||||
err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_DEFAULT);
|
||||
if (err || map.m_len == 0)
|
||||
return false;
|
||||
map.m_lblk += map.m_len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __get_data_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh, int create, int flag,
|
||||
pgoff_t *next_pgofs, int seg_type)
|
||||
|
@ -2308,7 +2326,7 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (__force_buffered_io(inode, rw))
|
||||
if (f2fs_force_buffered_io(inode, rw))
|
||||
return 0;
|
||||
|
||||
trace_f2fs_direct_IO_enter(inode, offset, count, rw);
|
||||
|
@ -2316,7 +2334,15 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
|||
if (rw == WRITE && whint_mode == WHINT_MODE_OFF)
|
||||
iocb->ki_hint = WRITE_LIFE_NOT_SET;
|
||||
|
||||
down_read(&F2FS_I(inode)->dio_rwsem[rw]);
|
||||
if (!down_read_trylock(&F2FS_I(inode)->dio_rwsem[rw])) {
|
||||
if (iocb->ki_flags & IOCB_NOWAIT) {
|
||||
iocb->ki_hint = hint;
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
down_read(&F2FS_I(inode)->dio_rwsem[rw]);
|
||||
}
|
||||
|
||||
err = blockdev_direct_IO(iocb, inode, iter, offset, get_data_block_dio);
|
||||
up_read(&F2FS_I(inode)->dio_rwsem[rw]);
|
||||
|
||||
|
@ -2332,6 +2358,7 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
|||
}
|
||||
}
|
||||
|
||||
out:
|
||||
trace_f2fs_direct_IO_exit(inode, offset, count, rw, err);
|
||||
|
||||
return err;
|
||||
|
|
|
@ -2961,6 +2961,7 @@ int f2fs_release_page(struct page *page, gfp_t wait);
|
|||
int f2fs_migrate_page(struct address_space *mapping, struct page *newpage,
|
||||
struct page *page, enum migrate_mode mode);
|
||||
#endif
|
||||
bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len);
|
||||
|
||||
/*
|
||||
* gc.c
|
||||
|
@ -3345,4 +3346,11 @@ static inline bool f2fs_may_encrypt(struct inode *inode)
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline bool f2fs_force_buffered_io(struct inode *inode, int rw)
|
||||
{
|
||||
return (f2fs_encrypted_file(inode) ||
|
||||
(rw == WRITE && test_opt(F2FS_I_SB(inode), LFS)) ||
|
||||
F2FS_I_SB(inode)->s_ndevs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -482,6 +482,9 @@ static int f2fs_file_open(struct inode *inode, struct file *filp)
|
|||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
filp->f_mode |= FMODE_NOWAIT;
|
||||
|
||||
return dquot_file_open(inode, filp);
|
||||
}
|
||||
|
||||
|
@ -2696,7 +2699,15 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
|
||||
return -EIO;
|
||||
|
||||
inode_lock(inode);
|
||||
if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT))
|
||||
return -EINVAL;
|
||||
|
||||
if (!inode_trylock(inode)) {
|
||||
if (iocb->ki_flags & IOCB_NOWAIT)
|
||||
return -EAGAIN;
|
||||
inode_lock(inode);
|
||||
}
|
||||
|
||||
ret = generic_write_checks(iocb, from);
|
||||
if (ret > 0) {
|
||||
int err;
|
||||
|
@ -2704,11 +2715,23 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
if (iov_iter_fault_in_readable(from, iov_iter_count(from)))
|
||||
set_inode_flag(inode, FI_NO_PREALLOC);
|
||||
|
||||
err = f2fs_preallocate_blocks(iocb, from);
|
||||
if (err) {
|
||||
clear_inode_flag(inode, FI_NO_PREALLOC);
|
||||
inode_unlock(inode);
|
||||
return err;
|
||||
if ((iocb->ki_flags & IOCB_NOWAIT) &&
|
||||
(iocb->ki_flags & IOCB_DIRECT)) {
|
||||
if (!f2fs_overwrite_io(inode, iocb->ki_pos,
|
||||
iov_iter_count(from)) ||
|
||||
f2fs_has_inline_data(inode) ||
|
||||
f2fs_force_buffered_io(inode, WRITE)) {
|
||||
inode_unlock(inode);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
} else {
|
||||
err = f2fs_preallocate_blocks(iocb, from);
|
||||
if (err) {
|
||||
clear_inode_flag(inode, FI_NO_PREALLOC);
|
||||
inode_unlock(inode);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
blk_start_plug(&plug);
|
||||
ret = __generic_file_write_iter(iocb, from);
|
||||
|
|
|
@ -144,6 +144,9 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate);
|
|||
/* File was opened by fanotify and shouldn't generate fanotify events */
|
||||
#define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
|
||||
|
||||
/* File is capable of returning -EAGAIN if I/O will block */
|
||||
#define FMODE_NOWAIT ((__force fmode_t)0x8000000)
|
||||
|
||||
/*
|
||||
* Flag for rw_copy_check_uvector and compat_rw_copy_check_uvector
|
||||
* that indicates that they should check the contents of the iovec are
|
||||
|
@ -334,6 +337,7 @@ enum rw_hint {
|
|||
#define IOCB_EVENTFD (1 << 0)
|
||||
#define IOCB_APPEND (1 << 1)
|
||||
#define IOCB_DIRECT (1 << 2)
|
||||
#define IOCB_NOWAIT (1 << 7)
|
||||
|
||||
struct kiocb {
|
||||
struct file *ki_filp;
|
||||
|
|
Loading…
Add table
Reference in a new issue