io-uring/xfs: support async buffered writes
Subsystems (10)
cover io-uring/xfs: support async buffered writes
This patch series adds support for async buffered writes when using both xfs and io-uring. Currently io-uring only supports buffered writes in the slow path, by processing them in the io workers. With this patch series it is now possible to support buffered writes in the fast path. To be able to use the fast path the required pages must be in the page cache, the required locks in xfs can be granted immediately and no additional blocks need to be read form disk.
Updating the inode can take time. An optimization has been implemented for the time update. Time updates will be processed in the slow path. While there is already a time update in process, other write requests for the same file, can skip the update of the modification time.
Performance results:
For fio the following results have been obtained with a queue depth of 1 and 4k block size (runtime 600 secs):
sequential writes:
without patch with patch
iops: 80k 269k
random writes:
without patch with patch
iops: 76k 249k
For an io depth of 1, the new patch improves throughput by over three times (compared to the exiting behavior, where buffered writes are processed by an io-worker process) and also the latency is considerably reduced. To achieve the same or better performance with the exisiting code an io depth of 4 is required. Increasing the iodepth further does not lead to any further improvements.
Especially for mixed workloads this is a considerable improvement.
Support for async buffered writes:
Patch 1: block: add check for async buffered writes to generic_write_checks
Add a new flag FMODE_BUF_WASYNC so filesystems can specify that they support async buffered writes and include the flag in the check of the function generic_write_checks().
Patch 2: mm: add FGP_ATOMIC flag to __filemap_get_folio()
This adds the FGP_ATOMIC flag. This allows to specify the gfp flags for memory allocations for async buffered writes.
Patch 3: iomap: add iomap_page_create_gfp to allocate iomap_pages
Add new function to allow specifying gfp flags when allocating and initializing the structure iomap_page.
Patch 4: iomap: use iomap_page_create_gfp() in __iomap_write_begin
Add a gfp flag to the iomap_page_create function.
Patch 5: iomap: add async buffered write support
Set IOMAP_NOWAIT flag if IOCB_NOWAIT is set. Also use specific gfp flags if the iomap_page structure is allocated for an async buffered page.
Patch 6: xfs: add iomap async buffered write support
Add async buffered write support to the xfs iomap layer.
Support for async buffered write support and inode time modification
Patch 7: fs: split off need_remove_file_privs() do_remove_file_privs()
Splits of a check and action function, so they can later be invoked in the nowait code path.
Patch 8: fs: split off need_file_update_time and do_file_update_time
Splits of a check and action function, so they can later be invoked in the nowait code path.
Patch 9: fs: add pending file update time flag.
Add new flag so consecutive write requests for the same inode can proceed without waiting for the inode modification time to complete.
Patch 10: xfs: enable async write file modification handling.
Enable async write handling in xfs for the file modification time update. If the file modification update requires logging or removal of privileges that needs to wait, -EAGAIN is returned.
Patch 11: xfs: add async buffered write support
Take the ilock in nowait mode if async buffered writes are enabled.
Patch 12: io_uring: add support for async buffered writes
This enables the async buffered writes optimization in io_uring. Buffered writes are enabled for blocks that are already in the page cache.
Patch 13: io_uring: Add tracepoint for short writes
Support for write throttling of async buffered writes:
Patch 14: sched: add new fields to task_struct
Add two new fields to the task_struct. These fields store the deadline after which writes are no longer throttled.
Patch 15: mm: support write throttling for async buffered writes
This changes the balance_dirty_pages function to take an additional parameter. When nowait is specified the write throttling code no longer waits synchronously for the deadline to expire. Instead it sets the fields in task_struct. Once the deadline expires the fields are reset.
Patch 16: iomap: User throttling for async buffered writes.
Enable async buffered write throttling in iomap.
Patch 17: io_uring: support write throttling for async buffered writes
Adds support to io_uring for write throttling. When the writes are throttled, the write requests are added to the pending io list. Once the write throttling deadline expires, the writes are submitted.
Enable async buffered write support in xfs
Patch 18: xfs: enable async buffered write support
This enables the flag that enables async buffered writes for xfs.
Testing:
This patch has been tested with xfstests and fio.
Stefan Roesch (18):
block: add check for async buffered writes to generic_write_checks
mm: add FGP_ATOMIC flag to __filemap_get_folio()
iomap: add iomap_page_create_gfp to allocate iomap_pages
iomap: use iomap_page_create_gfp() in __iomap_write_begin
iomap: add async buffered write support
xfs: add iomap async buffered write support
fs: split off need_remove_file_privs() do_remove_file_privs()
fs: split off need_file_update_time and do_file_update_time
fs: add pending file update time flag.
xfs: Enable async write file modification handling.
xfs: add async buffered write support
io_uring: add support for async buffered writes
io_uring: add tracepoint for short writes
sched: add new fields to task_struct
mm: support write throttling for async buffered writes
iomap: User throttling for async buffered writes.
io_uring: support write throttling for async buffered writes
xfs: enable async buffered write support
fs/inode.c | 127 +++++++++++++++++++++---------- fs/io_uring.c | 131 +++++++++++++++++++++++++++++--- fs/iomap/buffered-io.c | 63 +++++++++++++-- fs/read_write.c | 3 +- fs/xfs/xfs_file.c | 51 +++++++++++-- fs/xfs/xfs_iomap.c | 33 +++++++- include/linux/fs.h | 14 ++++ include/linux/pagemap.h | 1 + include/linux/sched.h | 3 + include/linux/writeback.h | 1 + include/trace/events/io_uring.h | 25 ++++++ kernel/fork.c | 1 + mm/filemap.c | 3 + mm/page-writeback.c | 54 +++++++++---- 14 files changed, 424 insertions(+), 86 deletions(-)
base-commit: af2d861d4cd2a4da5137f795ee3509e6f944a25b
Signature · 1 line
2.30.2
RFC PATCH 1/18 block: add check for async buffered writes to generic_write_checks
This introduces the flag FMODE_BUF_WASYNC. If devices support async buffered writes, this flag can be set. It also modifies the check in generic_write_checks to take async buffered writes into consideration.
--- fs/read_write.c | 3 ++- include/linux/fs.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-)
fs/read_write.c +2 -1
@@ -1633,7 +1633,8 @@ int generic_write_checks_count(struct kiocb *iocb, loff_t *count)if (iocb->ki_flags &IOCB_APPEND ) iocb->ki_pos =i_size_read (inode); -if ((iocb->ki_flags &IOCB_NOWAIT ) && !(iocb->ki_flags &IOCB_DIRECT )) +if ((iocb->ki_flags &IOCB_NOWAIT ) && + !((iocb->ki_flags &IOCB_DIRECT ) || (file->f_mode &FMODE_BUF_WASYNC )))return -EINVAL ;return generic_write_check_limits (iocb->ki_filp, iocb->ki_pos, count);
include/linux/fs.h +3 -0
@@ -177,6 +177,9 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,/* File supports async buffered reads */ #define FMODE_BUF_RASYNC ((__force fmode_t)0x40000000) +/* File supports async nowait buffered writes */ +#define FMODE_BUF_WASYNC ((__force fmode_t)0x80000000) +/* * Attribute flags. These should be or-ed together to figure out what * has been changed!
Signature · 1 line
2.30.2
RFC PATCH 2/18 mm: add FGP_ATOMIC flag to __filemap_get_folio()
In reply to Stefan RoeschDefine FGP_ATOMIC flag and add support for the new flag in the function __filemap_get_folio().
--- include/linux/pagemap.h | 1 + mm/filemap.c | 3 +++ 2 files changed, 4 insertions(+)
include/linux/pagemap.h +1 -0
@@ -509,6 +509,7 @@ pgoff_t page_cache_prev_miss(struct address_space *mapping,#define FGP_HEAD 0x00000080#define FGP_ENTRY 0x00000100#define FGP_STABLE 0x00000200 +#define FGP_ATOMIC 0x00000400struct folio *__filemap_get_folio (struct address_space *mapping,pgoff_t index,int fgp_flags,gfp_t gfp);
mm/filemap.c +3 -0
@@ -1929,6 +1929,7 @@ static void *mapping_get_entry(struct address_space *mapping, pgoff_t index)* * %FGP_NOFS - __GFP_FS will get cleared in gfp. * * %FGP_NOWAIT - Don't get blocked by page lock. * * %FGP_STABLE - Wait for the folio to be stable (finished writeback) +* * %FGP_ATOMIC - Use atomic allocations * * If %FGP_LOCK or %FGP_CREAT are specified then the function may sleep even * if the %GFP flags specified for %FGP_CREAT are atomic.
@@ -1988,6 +1989,8 @@ struct folio *__filemap_get_folio(struct address_space *mapping, pgoff_t index, gfp |= __GFP_WRITE;if (fgp_flags &FGP_NOFS ) gfp &= ~__GFP_FS; +if (fgp_flags &FGP_ATOMIC ) + gfp |=GFP_ATOMIC ; folio =filemap_alloc_folio (gfp,0 );if (!folio)
Signature · 1 line
2.30.2
Matthew Wilcox
maintainerRe: [RFC PATCH v1 02/18] mm: add FGP_ATOMIC flag to __filemap_get_folio()
Quoted 2 lines · Stefan Roesch
Define
FGP_ATOMICflag and add support for the new flag in the function__filemap_get_folio().
No. You don't get to use the emergency memory pools for doing writes. That completely screws up the MM.
Re: [RFC PATCH v1 02/18] mm: add FGP_ATOMIC flag to __filemap_get_folio()
Quoted 6 lines · Matthew Wilcox
Quoted 2 lines · Stefan Roesch
Define
FGP_ATOMICflag and add support for the new flag in the function__filemap_get_folio().No. You don't get to use the emergency memory pools for doing writes. That completely screws up the MM.
The next version of the patch will remove this patch from the patch series and the need of the atomic flag.
RFC PATCH 3/18 iomap: add iomap_page_create_gfp to allocate iomap_pages
In reply to Stefan RoeschAdd the function iomap_page_create_gfp() to be able to specify gfp flags and to pass in the number of blocks per folio in the function iomap_page_create_gfp().
No intended functional changes in this patch.
--- fs/iomap/buffered-io.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-)
fs/iomap/buffered-io.c +28 -6
@@ -43,17 +43,27 @@ static inline struct iomap_page *to_iomap_page(struct folio *folio)static struct bio_set iomap_ioend_bioset; +/** +* iomap_page_create_gfp : Create and initialize iomap_page for folio. +* @inode : Pointer to inode +* @folio : Pointer to folio +* @nr_blocks : Number of blocks in the folio +* @gfp : gfp allocation flags +* +* This function returns a newly allocated iomap for the folio with the settings +* specified in the gfp parameter. +* +**/ static struct iomap_page * -iomap_page_create (struct inode *inode,struct folio *folio) +iomap_page_create_gfp (struct inode *inode,struct folio *folio, +unsigned int nr_blocks,gfp_t gfp) { -struct iomap_page *iop =to_iomap_page (folio); -unsigned int nr_blocks =i_blocks_per_folio (inode, folio); +struct iomap_page *iop; -if (iop || nr_blocks <=1 ) + iop =kzalloc (struct_size (iop, uptodate,BITS_TO_LONGS (nr_blocks)), gfp); +if (!iop)return iop; - iop =kzalloc (struct_size (iop, uptodate,BITS_TO_LONGS (nr_blocks)), -GFP_NOFS | __GFP_NOFAIL);spin_lock_init (&iop->uptodate_lock);if (folio_test_uptodate (folio))bitmap_fill (iop->uptodate, nr_blocks);
@@ -61,6 +71,18 @@ iomap_page_create(struct inode *inode, struct folio *folio)return iop; } +static struct iomap_page * +iomap_page_create (struct inode *inode,struct folio *folio) +{ +struct iomap_page *iop =to_iomap_page (folio); +unsigned int nr_blocks =i_blocks_per_folio (inode, folio); + +if (iop || nr_blocks <=1 ) +return iop; + +return iomap_page_create_gfp (inode, folio, nr_blocks,GFP_NOFS | __GFP_NOFAIL); +} +static void iomap_page_release (struct folio *folio) {struct iomap_page *iop =folio_detach_private (folio);
Signature · 1 line
2.30.2
RFC PATCH 4/18 iomap: use iomap_page_create_gfp() in __iomap_write_begin
In reply to Stefan RoeschThis change uses the new iomap_page_create_gfp() function in the function __iomap_write_begin().
No intended functional changes in this patch.
--- fs/iomap/buffered-io.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
fs/iomap/buffered-io.c +6 -1
@@ -572,17 +572,22 @@ static int __iomap_write_begin(const struct iomap_iter *iter, loff_t pos, size_t len, struct folio *folio) {const struct iomap *srcmap =iomap_iter_srcmap (iter); -struct iomap_page *iop =iomap_page_create (iter->inode, folio); +struct iomap_page *iop =to_iomap_page (folio);loff_t block_size =i_blocksize (iter->inode);loff_t block_start =round_down (pos, block_size);loff_t block_end =round_up (pos + len, block_size); +unsigned int nr_blocks =i_blocks_per_folio (iter->inode, folio);size_t from =offset_in_folio (folio, pos), to = from + len;size_t poff, plen; +gfp_t gfp =GFP_NOFS | __GFP_NOFAIL;if (folio_test_uptodate (folio))return 0 ;folio_clear_error (folio); +if (!iop && nr_blocks >1 ) + iop =iomap_page_create_gfp (iter->inode, folio, nr_blocks, gfp); + do { iomap_adjust_read_range(iter->inode, folio, &block_start, block_end - block_start, &poff, &plen);
Signature · 1 line
2.30.2
RFC PATCH 5/18 iomap: add async buffered write support
In reply to Stefan RoeschThis adds async buffered write support to iomap. The support is focused on the changes necessary to support XFS with iomap.
Support for other filesystems might require additional changes.
--- fs/iomap/buffered-io.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-)
fs/iomap/buffered-io.c +20 -1
@@ -580,13 +580,20 @@ static int __iomap_write_begin(const struct iomap_iter *iter, loff_t pos,size_t from =offset_in_folio (folio, pos), to = from + len;size_t poff, plen;gfp_t gfp =GFP_NOFS | __GFP_NOFAIL; +bool no_wait = (iter->flags &IOMAP_NOWAIT ); + +if (no_wait) + gfp =GFP_NOIO |GFP_ATOMIC ;if (folio_test_uptodate (folio))return 0 ;folio_clear_error (folio); -if (!iop && nr_blocks >1 ) +if (!iop && nr_blocks >1 ) { iop =iomap_page_create_gfp (iter->inode, folio, nr_blocks, gfp); +if (no_wait && !iop) +return -EAGAIN ; + } do { iomap_adjust_read_range(iter->inode, folio, &block_start,
@@ -603,6 +610,8 @@ static int __iomap_write_begin(const struct iomap_iter *iter, loff_t pos,if (WARN_ON_ONCE (iter->flags &IOMAP_UNSHARE ))return -EIO ;folio_zero_segments (folio, poff, from, to, poff + plen); + }else if (no_wait) { +return -EAGAIN ; }else {int status =iomap_read_folio_sync (block_start, folio, poff, plen, srcmap);
@@ -633,6 +642,9 @@ static int iomap_write_begin(const struct iomap_iter *iter, loff_t pos,unsigned fgp =FGP_LOCK |FGP_WRITE |FGP_CREAT |FGP_STABLE |FGP_NOFS ;int status =0 ; +if (iter->flags &IOMAP_NOWAIT ) + fgp |=FGP_NOWAIT |FGP_ATOMIC ; +BUG_ON (pos + len > iter->iomap.offset + iter->iomap.length);if (srcmap != &iter->iomap)BUG_ON (pos + len > srcmap->offset + srcmap->length);
@@ -790,6 +802,10 @@ static loff_t iomap_write_iter(struct iomap_iter *iter, struct iov_iter *i)* Otherwise there's a nasty deadlock on copying from the * same page as we're writing to, without it being marked * up-to-date. +* +* For async buffered writes the assumption is that the user +* page has already been faulted in. This can be optimized by +* faulting the user page in the prepare phase of io-uring. */ if (unlikely (fault_in_iov_iter_readable (i, bytes) == bytes)) { status = -EFAULT ;
@@ -845,6 +861,9 @@ iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *i, };int ret; +if (iocb->ki_flags &IOCB_NOWAIT ) + iter.flags |=IOMAP_NOWAIT ; +while ((ret =iomap_iter (&iter, ops)) >0 ) iter.processed =iomap_write_iter (&iter, i);if (iter.pos == iocb->ki_pos)
Signature · 1 line
2.30.2
RFC PATCH 6/18 xfs: add iomap async buffered write support
In reply to Stefan RoeschThis adds the async buffered write support to the iomap layer of XFS. If a lock cannot be acquired or additional reads need to be performed, the request will return -EAGAIN in case this is an async buffered write request.
--- fs/xfs/xfs_iomap.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)
fs/xfs/xfs_iomap.c +31 -2
@@ -881,18 +881,28 @@ xfs_buffered_write_iomap_begin(bool eof = false, cow_eof = false, shared = false;int allocfork =XFS_DATA_FORK ;int error =0 ; +bool no_wait = (flags &IOMAP_NOWAIT );if (xfs_is_shutdown (mp))return -EIO ;/* we can't use delayed allocations when using extent size hints */ -if (xfs_get_extsz_hint (ip)) +if (xfs_get_extsz_hint (ip)) { +if (no_wait) +return -EAGAIN ; +return xfs_direct_write_iomap_begin (inode, offset, count, flags, iomap, srcmap); + }ASSERT (!XFS_IS_REALTIME_INODE (ip)); -xfs_ilock (ip,XFS_ILOCK_EXCL ); +if (no_wait) { +if (!xfs_ilock_nowait (ip,XFS_ILOCK_EXCL )) +return -EAGAIN ; + }else { +xfs_ilock (ip,XFS_ILOCK_EXCL ); + }if (XFS_IS_CORRUPT (mp, !xfs_ifork_has_extents (&ip->i_df)) ||XFS_TEST_ERROR (false, mp,XFS_ERRTAG_BMAPIFORMAT )) {
@@ -902,6 +912,11 @@ xfs_buffered_write_iomap_begin(XFS_STATS_INC (mp, xs_blk_mapw); +if (no_wait &&xfs_need_iread_extents (XFS_IFORK_PTR (ip,XFS_DATA_FORK ))) { + error = -EAGAIN ; + goto out_unlock; + } + error =xfs_iread_extents (NULL , ip,XFS_DATA_FORK );if (error) goto out_unlock;
@@ -933,6 +948,10 @@ xfs_buffered_write_iomap_begin(if (xfs_is_cow_inode (ip)) {if (!ip->i_cowfp) {ASSERT (!xfs_is_reflink_inode (ip)); +if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + }xfs_ifork_init_cow (ip); } cow_eof = !xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb,
@@ -956,6 +975,11 @@ xfs_buffered_write_iomap_begin( goto found_imap; } +if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + } +xfs_trim_extent (&imap, offset_fsb, end_fsb - offset_fsb);/* Trim the mapping to the nearest shared extent boundary. */
@@ -993,6 +1017,11 @@ xfs_buffered_write_iomap_begin( allocfork =XFS_COW_FORK ; } +if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + } + error =xfs_qm_dqattach_locked (ip, false);if (error) goto out_unlock;
Signature · 1 line
2.30.2
Re: [RFC PATCH v1 06/18] xfs: add iomap async buffered write support
Quoted 20 lines · Stefan Roesch
This adds the async buffered write support to the iomap layer of XFS. If a lock cannot be acquired or additional reads need to be performed, the request will return
-EAGAINin case this is an async buffered write request.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_iomap.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)fs/xfs/xfs_iomap.c +1
-0@@ -881,18 +881,28 @@ xfs_buffered_write_iomap_begin(bool eof = false, cow_eof = false, shared = false;int allocfork =XFS_DATA_FORK ;int error =0 ; +bool no_wait = (flags &IOMAP_NOWAIT );if (xfs_is_shutdown (mp))
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 895
Quoted 10 lines
fs/xfs/xfs_iomap.c +4
-1return -EIO ;/* we can't use delayed allocations when using extent size hints */ -if (xfs_get_extsz_hint (ip)) +if (xfs_get_extsz_hint (ip)) { +if (no_wait) +return -EAGAIN ; +return xfs_direct_write_iomap_begin (inode, offset, count, flags, iomap, srcmap);
Why can't we do IOMAP_NOWAIT allocation here? xfs_direct_write_iomap_begin() supports IOMAP_NOWAIT semantics just fine - that's what the direct IO path uses...
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 905
Quoted 11 lines
fs/xfs/xfs_iomap.c +7
-1+ }ASSERT (!XFS_IS_REALTIME_INODE (ip)); -xfs_ilock (ip,XFS_ILOCK_EXCL ); +if (no_wait) { +if (!xfs_ilock_nowait (ip,XFS_ILOCK_EXCL )) +return -EAGAIN ; + }else { +xfs_ilock (ip,XFS_ILOCK_EXCL ); + }
handled by xfs_ilock_for_iomap().
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 919
Quoted 12 lines
fs/xfs/xfs_iomap.c +5
-0if (XFS_IS_CORRUPT (mp, !xfs_ifork_has_extents (&ip->i_df)) ||XFS_TEST_ERROR (false, mp,XFS_ERRTAG_BMAPIFORMAT )) {@@ -902,6 +912,11 @@ xfs_buffered_write_iomap_begin(XFS_STATS_INC (mp, xs_blk_mapw); +if (no_wait &&xfs_need_iread_extents (XFS_IFORK_PTR (ip,XFS_DATA_FORK ))) { + error = -EAGAIN ; + goto out_unlock; + } +
Also handled by xfs_ilock_for_iomap().
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 956
Quoted 13 lines
fs/xfs/xfs_iomap.c +4
-0error =xfs_iread_extents (NULL , ip,XFS_DATA_FORK );if (error) goto out_unlock;@@ -933,6 +948,10 @@ xfs_buffered_write_iomap_begin(if (xfs_is_cow_inode (ip)) {if (!ip->i_cowfp) {ASSERT (!xfs_is_reflink_inode (ip)); +if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + }xfs_ifork_init_cow (ip); }
Also handled by xfs_ilock_for_iomap().
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 982
Quoted 10 lines
fs/xfs/xfs_iomap.c +5
-0cow_eof = !xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb,@@ -956,6 +975,11 @@ xfs_buffered_write_iomap_begin( goto found_imap; } +if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + } +
Won't get here because this is COW path, and xfs_ilock_for_iomap() handles returning -EAGAIN for that case.
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 1023
Quoted 11 lines
fs/xfs/xfs_iomap.c +4
-0xfs_trim_extent (&imap, offset_fsb, end_fsb - offset_fsb);/* Trim the mapping to the nearest shared extent boundary. */ @@ -993,6 +1017,11 @@ xfs_buffered_write_iomap_begin( allocfork =XFS_COW_FORK ; } +if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + }
Why? Delayed allocation doesn't block...
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [RFC PATCH v1 06/18] xfs: add iomap async buffered write support
Quoted 23 lines · Dave Chinner
Quoted 22 lines · Stefan Roesch
This adds the async buffered write support to the iomap layer of XFS. If a lock cannot be acquired or additional reads need to be performed, the request will return
-EAGAINin case this is an async buffered write request.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_iomap.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)fs/xfs/xfs_iomap.c +1
-0@@ -881,18 +881,28 @@ xfs_buffered_write_iomap_begin(bool eof = false, cow_eof = false, shared = false;int allocfork =XFS_DATA_FORK ;int error =0 ; +bool no_wait = (flags &IOMAP_NOWAIT );if (xfs_is_shutdown (mp))return -EIO ;
Quoted 12 lines
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 895
Quoted 8 lines
fs/xfs/xfs_iomap.c +4
-1/* we can't use delayed allocations when using extent size hints */ -if (xfs_get_extsz_hint (ip)) +if (xfs_get_extsz_hint (ip)) { +if (no_wait) +return -EAGAIN ; +return xfs_direct_write_iomap_begin (inode, offset, count, flags, iomap, srcmap);Why can't we do
IOMAP_NOWAITallocation here?xfs_direct_write_iomap_begin()supportsIOMAP_NOWAITsemantics just fine - that's what the direct IO path uses...
I'll make that change in the next version.
Quoted 5 lines
Quoted 5 lines
fs/xfs/xfs_iomap.c +1
-0+ }ASSERT (!XFS_IS_REALTIME_INODE (ip));
Quoted 9 lines
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 905
Quoted 7 lines
fs/xfs/xfs_iomap.c +6
-1-xfs_ilock (ip,XFS_ILOCK_EXCL ); +if (no_wait) { +if (!xfs_ilock_nowait (ip,XFS_ILOCK_EXCL )) +return -EAGAIN ; + }else { +xfs_ilock (ip,XFS_ILOCK_EXCL ); + }handled by
xfs_ilock_for_iomap().
The helper xfs_ilock_for_iomap cannot be used for this purpose. The function xfs_ilock_for_iomap tries to deduce the lock mode. For the function xfs_file_buffered_write() the lock mode must be exclusive. However this is not always the case when the helper xfs_ilock_for_iomap is used. There are cases where xfs_is_cow_inode(() is not true.
I'll add a new helper that will be used here.
Quoted 7 lines
Quoted 7 lines
fs/xfs/xfs_iomap.c +0
-0if (XFS_IS_CORRUPT (mp, !xfs_ifork_has_extents (&ip->i_df)) ||XFS_TEST_ERROR (false, mp,XFS_ERRTAG_BMAPIFORMAT )) {@@ -902,6 +912,11 @@ xfs_buffered_write_iomap_begin(XFS_STATS_INC (mp, xs_blk_mapw);
Quoted 7 lines
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 919
Quoted 5 lines
fs/xfs/xfs_iomap.c +5
-0+if (no_wait &&xfs_need_iread_extents (XFS_IFORK_PTR (ip,XFS_DATA_FORK ))) { + error = -EAGAIN ; + goto out_unlock; + } +Also handled by
xfs_ilock_for_iomap().
I'll remove this check with the next version.
Quoted 17 lines
Quoted 5 lines
fs/xfs/xfs_iomap.c +0
-0error =xfs_iread_extents (NULL , ip,XFS_DATA_FORK );if (error) goto out_unlock;fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 956
Quoted 9 lines
fs/xfs/xfs_iomap.c +4
-0@@ -933,6 +948,10 @@ xfs_buffered_write_iomap_begin(if (xfs_is_cow_inode (ip)) {if (!ip->i_cowfp) {ASSERT (!xfs_is_reflink_inode (ip)); +if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + }xfs_ifork_init_cow (ip); }Also handled by
xfs_ilock_for_iomap().
I'll remove this check with the next version.
Quoted 5 lines
Quoted 5 lines
fs/xfs/xfs_iomap.c +0
-0cow_eof = !xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb,@@ -956,6 +975,11 @@ xfs_buffered_write_iomap_begin( goto found_imap; }
Quoted 8 lines
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 982
Quoted 5 lines
fs/xfs/xfs_iomap.c +5
-0+if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + } +Won't get here because this is COW path, and
xfs_ilock_for_iomap()handles returning-EAGAINfor that case.
I'll remove this check with the next version.
Quoted 8 lines
Quoted 8 lines
fs/xfs/xfs_iomap.c +0
-0xfs_trim_extent (&imap, offset_fsb, end_fsb - offset_fsb);/* Trim the mapping to the nearest shared extent boundary. */ @@ -993,6 +1017,11 @@ xfs_buffered_write_iomap_begin( allocfork =XFS_COW_FORK ; }
Quoted 6 lines
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 954
Quoted 4 lines
fs/xfs/xfs_iomap.c +4
-0+if (no_wait) { + error = -EAGAIN ; + goto out_unlock; + }Why? Delayed allocation doesn't block...
I'll remove this check with the next version.
Quoted 5 lines
Cheers,
Dave.
Re: [RFC PATCH v1 06/18] xfs: add iomap async buffered write support
Quoted 55 lines · Stefan Roesch
Quoted 23 lines · Dave Chinner
Quoted 22 lines · Stefan Roesch
This adds the async buffered write support to the iomap layer of XFS. If a lock cannot be acquired or additional reads need to be performed, the request will return
-EAGAINin case this is an async buffered write request.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_iomap.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)fs/xfs/xfs_iomap.c +1
-0@@ -881,18 +881,28 @@ xfs_buffered_write_iomap_begin(bool eof = false, cow_eof = false, shared = false;int allocfork =XFS_DATA_FORK ;int error =0 ; +bool no_wait = (flags &IOMAP_NOWAIT );if (xfs_is_shutdown (mp))return -EIO ;Quoted 12 lines
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 895
Quoted 8 lines
fs/xfs/xfs_iomap.c +4
-1/* we can't use delayed allocations when using extent size hints */ -if (xfs_get_extsz_hint (ip)) +if (xfs_get_extsz_hint (ip)) { +if (no_wait) +return -EAGAIN ; +return xfs_direct_write_iomap_begin (inode, offset, count, flags, iomap, srcmap);Why can't we do
IOMAP_NOWAITallocation here?xfs_direct_write_iomap_begin()supportsIOMAP_NOWAITsemantics just fine - that's what the direct IO path uses...I'll make that change in the next version.
Quoted 5 lines
Quoted 5 lines
fs/xfs/xfs_iomap.c +1
-0+ }ASSERT (!XFS_IS_REALTIME_INODE (ip));Quoted 9 lines
fs/xfs/xfs_iomap.c xfs_buffered_write_iomap_begin( · line 905
Quoted 7 lines
fs/xfs/xfs_iomap.c +6
-1-xfs_ilock (ip,XFS_ILOCK_EXCL ); +if (no_wait) { +if (!xfs_ilock_nowait (ip,XFS_ILOCK_EXCL )) +return -EAGAIN ; + }else { +xfs_ilock (ip,XFS_ILOCK_EXCL ); + }handled by
xfs_ilock_for_iomap().
Quoted 5 lines
The helper
xfs_ilock_for_iomapcannot be used for this purpose. The functionxfs_ilock_for_iomaptries to deduce the lock mode. For the functionxfs_file_buffered_write()the lock mode must be exclusive. However this is not always the case when the helperxfs_ilock_for_iomapis used. There are cases where xfs_is_cow_inode(() is not true.
That's trivially fixed by having the caller initialise the lock_mode to what they want, rather than have the function set the initial type to SHARED....
Quoted 1 line
I'll add a new helper that will be used here.
... so I don't think a new helper is needed.
--Dave.
Signature · 2 lines
Dave Chinner [email protected]
RFC PATCH 7/18 fs: split off need_remove_file_privs() do_remove_file_privs()
In reply to Stefan RoeschThis splits off the function need_remove_file_privs() from the function do_remove_file_privs() from the function file_remove_privs().
No intended functional changes in this patch.
--- fs/inode.c | 56 +++++++++++++++++++++++++++++++--------------- include/linux/fs.h | 3 +++ 2 files changed, 41 insertions(+), 18 deletions(-)
fs/inode.c +38 -18
@@ -2010,17 +2010,8 @@ static int __remove_privs(struct user_namespace *mnt_userns,return notify_change (mnt_userns, dentry, &newattrs,NULL ); } -/* -* Remove special file priviledges (suid, capabilities) when file is written -* to or truncated. -*/ -int file_remove_privs (struct file *file) +int need_file_remove_privs (struct inode *inode,struct dentry *dentry) { -struct dentry *dentry =file_dentry (file); -struct inode *inode =file_inode (file); -int kill; -int error =0 ; -/* * Fast path for nothing security related. * As well for non-regular files, e.g. blkdev inodes.
@@ -2030,16 +2021,37 @@ int file_remove_privs(struct file *file)if (IS_NOSEC (inode) || !S_ISREG (inode->i_mode))return 0 ; - kill =dentry_needs_remove_privs (dentry); -if (kill <0 ) -return kill; -if (kill) - error =__remove_privs (file_mnt_user_ns (file), dentry, kill); +return dentry_needs_remove_privs (dentry); +} + +int do_file_remove_privs (struct file *file,struct inode *inode, +struct dentry *dentry,int kill) +{ +int error =0 ; + + error =__remove_privs (file_mnt_user_ns (file), dentry, kill);if (!error)inode_has_no_xattr (inode);return error; } + +/* +* Remove special file privileges (suid, capabilities) when file is written +* to or truncated. +*/ +int file_remove_privs (struct file *file) +{ +struct dentry *dentry =file_dentry (file); +struct inode *inode =file_inode (file); +int kill; + + kill =need_file_remove_privs (inode, dentry); +if (kill <=0 ) +return kill; + +return do_file_remove_privs (file, inode, dentry, kill); +}EXPORT_SYMBOL (file_remove_privs);/**
@@ -2094,14 +2106,22 @@ EXPORT_SYMBOL(file_update_time);int file_modified (struct file *file) {int err; +int ret; +struct dentry *dentry =file_dentry (file); +struct inode *inode =file_inode (file);/* * Clear the security bits if the process is not being run by root. * This keeps people from modifying setuid and setgid binaries. */ - err =file_remove_privs (file); -if (err) -return err; + ret =need_file_remove_privs (inode, dentry); +if (ret <0 ) { +return ret; + }else if (ret >0 ) { + ret =do_file_remove_privs (file, inode, dentry, ret); +if (ret) +return ret; + }if (unlikely (file->f_mode &FMODE_NOCMTIME ))return 0 ;
include/linux/fs.h +3 -0
@@ -2381,6 +2381,9 @@ static inline void file_accessed(struct file *file)touch_atime (&file->f_path); } +extern int need_file_remove_privs (struct inode *inode,struct dentry *dentry); +extern int do_file_remove_privs (struct file *file,struct inode *inode, +struct dentry *dentry,int kill);extern int file_modified (struct file *file);int sync_inode_metadata (struct inode *inode,int wait);
Signature · 1 line
2.30.2
RFC PATCH 8/18 fs: split off need_file_update_time and do_file_update_time
In reply to Stefan RoeschThis splits off the functions need_file_update_time() and do_file_update_time() from the function file_update_time().
This is required to support async buffered writes. No intended functional changes in this patch.
--- fs/inode.c | 72 ++++++++++++++++++++++++++++++---------------- include/linux/fs.h | 5 ++++ 2 files changed, 52 insertions(+), 25 deletions(-)
fs/inode.c +47 -25
@@ -2054,35 +2054,22 @@ int file_remove_privs(struct file *file) }EXPORT_SYMBOL (file_remove_privs); -/** -* file_update_time - update mtime and ctime time -* @file: file accessed -* -* Update the mtime and ctime members of an inode and mark the inode -* for writeback. Note that this function is meant exclusively for -* usage in the file write path of filesystems, and filesystems may -* choose to explicitly ignore update via this function with the -* S_NOCMTIME inode flag, e.g. for network filesystem where these -* timestamps are handled by the server. This can return an error for -* file systems who need to allocate space in order to update an inode. -*/ - -int file_update_time (struct file *file) +int need_file_update_time (struct inode *inode,struct file *file, +struct timespec64 *now) { -struct inode *inode =file_inode (file); -struct timespec64 now;int sync_it =0 ; -int ret; + +if (unlikely (file->f_mode &FMODE_NOCMTIME )) +return 0 ;/* First try to exhaust all avenues to not sync */ if (IS_NOCMTIME (inode))return 0 ; - now =current_time (inode); -if (!timespec64_equal (&inode->i_mtime, &now)) +if (!timespec64_equal (&inode->i_mtime, now)) sync_it =S_MTIME ; -if (!timespec64_equal (&inode->i_ctime, &now)) +if (!timespec64_equal (&inode->i_ctime, now)) sync_it |=S_CTIME ;if (IS_I_VERSION (inode) &&inode_iversion_need_inc (inode))
@@ -2091,24 +2078,58 @@ int file_update_time(struct file *file)if (!sync_it)return 0 ; +return sync_it; +} + +int do_file_update_time (struct inode *inode,struct file *file, +struct timespec64 *now,int sync_mode) +{ +int ret; +/* Finally allowed to write? Takes lock. */ if (__mnt_want_write_file (file))return 0 ; - ret =inode_update_time (inode, &now, sync_it); + ret =inode_update_time (inode, now, sync_mode);__mnt_drop_write_file (file);return ret; } + +/** +* file_update_time - update mtime and ctime time +* @file: file accessed +* +* Update the mtime and ctime members of an inode and mark the inode +* for writeback. Note that this function is meant exclusively for +* usage in the file write path of filesystems, and filesystems may +* choose to explicitly ignore update via this function with the +* S_NOCMTIME inode flag, e.g. for network filesystem where these +* timestamps are handled by the server. This can return an error for +* file systems who need to allocate space in order to update an inode. +*/ + +int file_update_time (struct file *file) +{ +int err; +struct inode *inode =file_inode (file); +struct timespec64 now =current_time (inode); + + err =need_file_update_time (inode, file, &now); +if (err <0 ) +return err; + +return do_file_update_time (inode, file, &now, err); +}EXPORT_SYMBOL (file_update_time);/* Caller must hold the file's inode lock */ int file_modified (struct file *file) { -int err;int ret;struct dentry *dentry =file_dentry (file);struct inode *inode =file_inode (file); +struct timespec64 now =current_time (inode);/* * Clear the security bits if the process is not being run by root.
@@ -2123,10 +2144,11 @@ int file_modified(struct file *file)return ret; } -if (unlikely (file->f_mode &FMODE_NOCMTIME )) -return 0 ; + ret =need_file_update_time (inode, file, &now); +if (ret <=0 ) +return ret; -return file_update_time (file); +return do_file_update_time (inode, file, &now, ret); }EXPORT_SYMBOL (file_modified);
include/linux/fs.h +5 -0
@@ -2384,6 +2384,11 @@ static inline void file_accessed(struct file *file)extern int need_file_remove_privs (struct inode *inode,struct dentry *dentry);extern int do_file_remove_privs (struct file *file,struct inode *inode,struct dentry *dentry,int kill); +extern int need_file_update_time (struct inode *inode,struct file *file, +struct timespec64 *now); +extern int do_file_update_time (struct inode *inode,struct file *file, +struct timespec64 *now,int sync_mode); +extern int file_modified (struct file *file);int sync_inode_metadata (struct inode *inode,int wait);
Signature · 1 line
2.30.2
RFC PATCH 9/18 fs: add pending file update time flag.
In reply to Stefan RoeschThis introduces an optimization for the update time flag and async buffered writes. While an update of the file modification time is pending and is handled by the workers, concurrent writes do not need to wait for this time update to complete.
--- fs/inode.c | 1 + include/linux/fs.h | 3 +++ 2 files changed, 4 insertions(+)
fs/inode.c +1 -0
@@ -2091,6 +2091,7 @@ int do_file_update_time(struct inode *inode, struct file *file,return 0 ; ret =inode_update_time (inode, now, sync_mode); + inode->i_flags &= ~S_PENDING_TIME ;__mnt_drop_write_file (file);return ret;
include/linux/fs.h +3 -0
@@ -2141,6 +2141,8 @@ struct super_operations {#define S_CASEFOLD (1 << 15)/* Casefolded file */ #define S_VERITY (1 << 16)/* Verity file (using fs/verity/) */ #define S_KERNEL_FILE (1 << 17)/* File is in use by the kernel (eg. fs/cachefiles) */ +#define S_PENDING_TIME (1 << 18)/* File update time is pending */ +/* * Note that nosuid etc flags are inode-specific: setting some file-system
@@ -2183,6 +2185,7 @@ static inline bool sb_rdonly(const struct super_block *sb) { return sb->s_flags#define IS_ENCRYPTED (inode) ((inode)->i_flags & S_ENCRYPTED)#define IS_CASEFOLDED (inode) ((inode)->i_flags & S_CASEFOLD)#define IS_VERITY (inode) ((inode)->i_flags & S_VERITY) +#define IS_PENDING_TIME (inode) ((inode)->i_flags & S_PENDING_TIME)#define IS_WHITEOUT (inode) (S_ISCHR(inode->i_mode) && \ (inode)->i_rdev == WHITEOUT_DEV)
Signature · 1 line
2.30.2
RFC PATCH 10/18 xfs: Enable async write file modification handling.
In reply to Stefan RoeschThis modifies xfs write checks to return -EAGAIN if the request either requires to remove privileges or needs to update the file modification time. This is required for async buffered writes, so the request gets handled in the io worker.
--- fs/xfs/xfs_file.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-)
fs/xfs/xfs_file.c +38 -1
@@ -299,6 +299,43 @@ xfs_file_read_iter(return ret; } +static int xfs_file_modified (struct file *file,int flags) +{ +int ret; +struct dentry *dentry =file_dentry (file); +struct inode *inode =file_inode (file); +struct timespec64 now =current_time (inode); + +/* +* Clear the security bits if the process is not being run by root. +* This keeps people from modifying setuid and setgid binaries. +*/ + ret =need_file_remove_privs (inode, dentry); +if (ret <0 ) { +return ret; + }else if (ret >0 ) { +if (flags &IOCB_NOWAIT ) +return -EAGAIN ; + + ret =do_file_remove_privs (file, inode, dentry, ret); +if (ret) +return ret; + } + + ret =need_file_update_time (inode, file, &now); +if (ret <=0 ) +return ret; +if (flags &IOCB_NOWAIT ) { +if (IS_PENDING_TIME (inode)) +return 0 ; + + inode->i_flags |=S_PENDING_TIME ; +return -EAGAIN ; + } + +return do_file_update_time (inode, file, &now, ret); +} +/* * Common pre-write limit and setup checks. *
@@ -410,7 +447,7 @@ xfs_file_write_checks(spin_unlock (&ip->i_flags_lock); out: -return file_modified (file); +return xfs_file_modified (file, iocb->ki_flags); } static int
Signature · 1 line
2.30.2
Re: [RFC PATCH v1 10/18] xfs: Enable async write file modification handling.
Quoted 44 lines · Stefan Roesch
This modifies xfs write checks to return
-EAGAINif the request either requires to remove privileges or needs to update the file modification time. This is required for async buffered writes, so the request gets handled in the io worker.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_file.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-)fs/xfs/xfs_file.c +26
-0@@ -299,6 +299,43 @@ xfs_file_read_iter(return ret; } +static int xfs_file_modified (struct file *file,int flags) +{ +int ret; +struct dentry *dentry =file_dentry (file); +struct inode *inode =file_inode (file); +struct timespec64 now =current_time (inode); + +/* +* Clear the security bits if the process is not being run by root. +* This keeps people from modifying setuid and setgid binaries. +*/ + ret =need_file_remove_privs (inode, dentry); +if (ret <0 ) { +return ret; + }else if (ret >0 ) { +if (flags &IOCB_NOWAIT ) +return -EAGAIN ; + + ret =do_file_remove_privs (file, inode, dentry, ret); +if (ret) +return ret; + } + + ret =need_file_update_time (inode, file, &now); +if (ret <=0 ) +return ret;
fs/xfs/xfs_file.c xfs_file_read_iter( · line 337
Quoted 10 lines
fs/xfs/xfs_file.c +10
-0+if (flags &IOCB_NOWAIT ) { +if (IS_PENDING_TIME (inode)) +return 0 ; + + inode->i_flags |=S_PENDING_TIME ; +return -EAGAIN ; + } + +return do_file_update_time (inode, file, &now, ret); +}
This has nothing XFS specific in it. It belongs in the VFS code.
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
Christian Brauner
maintainerRe: [RFC PATCH v1 10/18] xfs: Enable async write file modification handling.
In reply to Stefan RoeschQuoted 15 lines · Stefan Roesch
This modifies xfs write checks to return
-EAGAINif the request either requires to remove privileges or needs to update the file modification time. This is required for async buffered writes, so the request gets handled in the io worker.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_file.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-)fs/xfs/xfs_file.c +0
-0
fs/xfs/xfs_file.c xfs_file_read_iter( · line 302
Quoted 4 lines
fs/xfs/xfs_file.c +1
-0@@ -299,6 +299,43 @@ xfs_file_read_iter(return ret; } +static int xfs_file_modified (struct file *file,int flags)
This should probably be in fs/inode.c as:
int file_modified_async(struct file *file, int flags)
and then file_modified() can simply become:
int file_modified(struct file *file)
{
return file_modified_async(file, 0);
}or even:
int file_modified_async(struct file *file, bool async)
int file_modified(struct file *file)
{
return file_modified_async(file, false);
}to avoid piecing this together specifically in xfs (as Dave mentioned this is all pretty generic).
kernel test robot
build botRe: [RFC PATCH v1 10/18] xfs: Enable async write file modification handling.
In reply to Stefan Roeschreproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross #…
Hi Stefan,
[FYI, it's a private test report for your RFC patch.] [auto build test ERROR on af2d861d4cd2a4da5137f795ee3509e6f944a25b]
url: https://github.com/intel-lab-lkp/linux/commits/Stefan-Roesch/io-uring-xfs-support-async-buffered-writes/20220427-014610 base: af2d861d4cd2a4da5137f795ee3509e6f944a25b config: i386-randconfig-a006-20220425 (https://download.01.org/0day-ci/archive/20220427/[email protected]/config) compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project 1cddcfdc3c683b393df1a5c9063252eb60e52818)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/322e4a99c7828050895c8af8e49ac61cd6f7e0ea
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Stefan-Roesch/io-uring-xfs-support-async-buffered-writes/20220427-014610 git checkout 322e4a99c7828050895c8af8e49ac61cd6f7e0ea
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash
If you fix the issue, kindly add following tag as appropriate
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: "do_file_update_time" [fs/xfs/xfs.ko] undefined! >> ERROR: modpost: "need_file_update_time" [fs/xfs/xfs.ko] undefined! >> ERROR: modpost: "do_file_remove_privs" [fs/xfs/xfs.ko] undefined! >> ERROR: modpost: "need_file_remove_privs" [fs/xfs/xfs.ko] undefined!
Signature · 2 lines
0-DAY CI Kernel Test Service https://01.org/lkp
RFC PATCH 11/18 xfs: add async buffered write support
In reply to Stefan RoeschThis adds the async buffered write support to XFS. For async buffered write requests, the request will return -EAGAIN if the ilock cannot be obtained immediately.
--- fs/xfs/xfs_file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
fs/xfs/xfs_file.c +6 -4
@@ -739,12 +739,14 @@ xfs_file_buffered_write(bool cleared_space = false;int iolock; -if (iocb->ki_flags &IOCB_NOWAIT ) -return -EOPNOTSUPP ; - write_retry: iolock =XFS_IOLOCK_EXCL ; -xfs_ilock (ip, iolock); +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!xfs_ilock_nowait (ip, iolock)) +return -EAGAIN ; + }else { +xfs_ilock (ip, iolock); + } ret =xfs_file_write_checks (iocb, from, &iolock);if (ret)
Signature · 1 line
2.30.2
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 19 lines · Stefan Roesch
This adds the async buffered write support to XFS. For async buffered write requests, the request will return
-EAGAINif the ilock cannot be obtained immediately.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)fs/xfs/xfs_file.c +0
-2@@ -739,12 +739,14 @@ xfs_file_buffered_write(bool cleared_space = false;int iolock; -if (iocb->ki_flags &IOCB_NOWAIT ) -return -EOPNOTSUPP ;
fs/xfs/xfs_file.c xfs_file_buffered_write( · line 749
Quoted 10 lines
fs/xfs/xfs_file.c +6
-2- write_retry: iolock =XFS_IOLOCK_EXCL ; -xfs_ilock (ip, iolock); +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!xfs_ilock_nowait (ip, iolock)) +return -EAGAIN ; + }else { +xfs_ilock (ip, iolock); + }
xfs_ilock_iocb().
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 18 lines · Dave Chinner
Quoted 17 lines · Stefan Roesch
This adds the async buffered write support to XFS. For async buffered write requests, the request will return
-EAGAINif the ilock cannot be obtained immediately.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)fs/xfs/xfs_file.c +0
-0@@ -739,12 +739,14 @@ xfs_file_buffered_write(bool cleared_space = false;int iolock;
Quoted 15 lines
fs/xfs/xfs_file.c xfs_file_buffered_write( · line 749
Quoted 12 lines
fs/xfs/xfs_file.c +6
-4-if (iocb->ki_flags &IOCB_NOWAIT ) -return -EOPNOTSUPP ; - write_retry: iolock =XFS_IOLOCK_EXCL ; -xfs_ilock (ip, iolock); +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!xfs_ilock_nowait (ip, iolock)) +return -EAGAIN ; + }else { +xfs_ilock (ip, iolock); + }
xfs_ilock_iocb().
The helper xfs_ilock_iocb cannot be used as it hardcoded to use iocb->ki_filp to get a pointer to the xfs_inode. However here we need to use iocb->ki_filp->f_mapping->host. I'll split off new helper for this in the next version of the patch.
Quoted 2 lines
-Dave.
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 37 lines · Stefan Roesch
Quoted 18 lines · Dave Chinner
Quoted 17 lines · Stefan Roesch
This adds the async buffered write support to XFS. For async buffered write requests, the request will return
-EAGAINif the ilock cannot be obtained immediately.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)fs/xfs/xfs_file.c +0
-0@@ -739,12 +739,14 @@ xfs_file_buffered_write(bool cleared_space = false;int iolock;Quoted 15 lines
fs/xfs/xfs_file.c xfs_file_buffered_write( · line 749
Quoted 12 lines
fs/xfs/xfs_file.c +6
-4-if (iocb->ki_flags &IOCB_NOWAIT ) -return -EOPNOTSUPP ; - write_retry: iolock =XFS_IOLOCK_EXCL ; -xfs_ilock (ip, iolock); +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!xfs_ilock_nowait (ip, iolock)) +return -EAGAIN ; + }else { +xfs_ilock (ip, iolock); + }
xfs_ilock_iocb().
Quoted 2 lines
The helper
xfs_ilock_iocbcannot be used as it hardcoded to useiocb->ki_filpto get a pointer to thexfs_inode.
And the problem with that is?
I mean, look at what xfs_file_buffered_write() does to get the xfs_inode 10 lines about that change:
xfs_file_buffered_write(
struct kiocb *iocb,
struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host;
struct xfs_inode *ip = XFS_I(inode);In what cases does file_inode(iocb->ki_filp) point to a different inode than iocb->ki_filp->f_mapping->host? The dio write path assumes that file_inode(iocb->ki_filp) is correct, as do both the buffered and dio read paths.
What makes the buffered write path special in that file_inode(iocb->ki_filp) is not correctly set whilst iocb->ki_filp->f_mapping->host is?
Regardless, if this is a problem, then just pass the XFS inode to xfs_ilock_iocb() and this is a moot point.
Quoted 2 lines
However here we need to use
iocb->ki_filp->f_mapping->host. I'll split off new helper for this in the next version of the patch.
We don't need a new helper here, either.
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 55 lines · Dave Chinner
Quoted 37 lines · Stefan Roesch
Quoted 18 lines · Dave Chinner
Quoted 17 lines · Stefan Roesch
This adds the async buffered write support to XFS. For async buffered write requests, the request will return
-EAGAINif the ilock cannot be obtained immediately.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)fs/xfs/xfs_file.c +0
-0@@ -739,12 +739,14 @@ xfs_file_buffered_write(bool cleared_space = false;int iolock;Quoted 15 lines
fs/xfs/xfs_file.c xfs_file_buffered_write( · line 749
Quoted 12 lines
fs/xfs/xfs_file.c +6
-4-if (iocb->ki_flags &IOCB_NOWAIT ) -return -EOPNOTSUPP ; - write_retry: iolock =XFS_IOLOCK_EXCL ; -xfs_ilock (ip, iolock); +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!xfs_ilock_nowait (ip, iolock)) +return -EAGAIN ; + }else { +xfs_ilock (ip, iolock); + }
xfs_ilock_iocb().Quoted 2 lines
The helper
xfs_ilock_iocbcannot be used as it hardcoded to useiocb->ki_filpto get a pointer to thexfs_inode.And the problem with that is?
I mean, look at what
xfs_file_buffered_write()does to get thexfs_inode10 lines about that change:xfs_file_buffered_write(
struct kiocb *iocb,
structiov_iter*from){ struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; struct xfs_inode *ip = XFS_I(inode);
Quoted 9 lines
In what cases does
file_inode(iocb->ki_filp)point to a different inode thaniocb->ki_filp->f_mapping->host? The dio write path assumes thatfile_inode(iocb->ki_filp)is correct, as do both the buffered and dio read paths.What makes the buffered write path special in that
file_inode(iocb->ki_filp)is not correctly set whilstiocb->ki_filp->f_mapping->hostis?
In the function xfs_file_buffered_write() the code calls the function xfs_ilock(). The xfs_inode pointer that is passed in is iocb->ki_filp->f_mapping->host. The one used in xfs_ilock_iocb is ki_filp->f_inode.
After getting the lock, the code in xfs_file_buffered_write calls the function xfs_buffered_write_iomap_begin(). In this function the code calls xfs_ilock() for ki_filp->f_inode in exclusive mode.
If I replace the first xfs_ilock() call with xfs_ilock_iocb(), then it looks like I get a deadlock.
Am I missing something?
I can:
- replace the pointer to iocb with pointer to xfs_inode in the function xfs_ilock_iocb() and also pass in the flags value as a parameter.
or
- create function xfs_ilock_inode(), which xfs_ilock_iocb() calls. The existing calls will not need to change, only the xfs_ilock in xfs_file_buffered_write() will use xfs_ilock_inode().
Quoted 11 lines
Regardless, if this is a problem, then just pass the XFS inode to
xfs_ilock_iocb()and this is a moot point.Quoted 2 lines
However here we need to use
iocb->ki_filp->f_mapping->host. I'll split off new helper for this in the next version of the patch.We don't need a new helper here, either.
Cheers,
Dave.
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 72 lines · Stefan Roesch
Quoted 55 lines · Dave Chinner
Quoted 37 lines · Stefan Roesch
Quoted 18 lines · Dave Chinner
Quoted 17 lines · Stefan Roesch
This adds the async buffered write support to XFS. For async buffered write requests, the request will return
-EAGAINif the ilock cannot be obtained immediately.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)fs/xfs/xfs_file.c +0
-0@@ -739,12 +739,14 @@ xfs_file_buffered_write(bool cleared_space = false;int iolock;Quoted 15 lines
fs/xfs/xfs_file.c xfs_file_buffered_write( · line 749
Quoted 12 lines
fs/xfs/xfs_file.c +6
-4-if (iocb->ki_flags &IOCB_NOWAIT ) -return -EOPNOTSUPP ; - write_retry: iolock =XFS_IOLOCK_EXCL ; -xfs_ilock (ip, iolock); +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!xfs_ilock_nowait (ip, iolock)) +return -EAGAIN ; + }else { +xfs_ilock (ip, iolock); + }
xfs_ilock_iocb().Quoted 2 lines
The helper
xfs_ilock_iocbcannot be used as it hardcoded to useiocb->ki_filpto get a pointer to thexfs_inode.And the problem with that is?
I mean, look at what
xfs_file_buffered_write()does to get thexfs_inode10 lines about that change:xfs_file_buffered_write(
struct kiocb *iocb,
structiov_iter*from){ struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; struct xfs_inode *ip = XFS_I(inode);Quoted 9 lines
In what cases does
file_inode(iocb->ki_filp)point to a different inode thaniocb->ki_filp->f_mapping->host? The dio write path assumes thatfile_inode(iocb->ki_filp)is correct, as do both the buffered and dio read paths.What makes the buffered write path special in that
file_inode(iocb->ki_filp)is not correctly set whilstiocb->ki_filp->f_mapping->hostis?In the function
xfs_file_buffered_write()the code calls the functionxfs_ilock(). Thexfs_inodepointer that is passed in isiocb->ki_filp->f_mapping->host. The one used inxfs_ilock_iocbiski_filp->f_inode.
Quoted 8 lines
After getting the lock, the code in
xfs_file_buffered_writecalls the functionxfs_buffered_write_iomap_begin(). In this function the code callsxfs_ilock()forki_filp->f_inodein exclusive mode.If I replace the first
xfs_ilock()call withxfs_ilock_iocb(), then it looks like I get a deadlock.Am I missing something?
Yes. They take different locks. xfs_file_buffered_write() takes the IOLOCK, xfs_buffered_write_iomap_begin() takes the ILOCK....
Quoted 7 lines
I can:
- replace the pointer to iocb with pointer toxfs_inodein the functionxfs_ilock_iocb()and also pass in the flags value as a parameter.
or
- create functionxfs_ilock_inode(), whichxfs_ilock_iocb()calls. The existing calls will not need to change, only thexfs_ilockinxfs_file_buffered_write()will usexfs_ilock_inode().
Quoted 2 lines · You're making this way more complex than it needs to be. As I said:
Quoted 2 lines
Regardless, if this is a problem, then just pass the XFS inode to
xfs_ilock_iocb()and this is a moot point.
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 77 lines · Dave Chinner
Quoted 76 lines · Stefan Roesch
Quoted 55 lines · Dave Chinner
Quoted 37 lines · Stefan Roesch
Quoted 18 lines · Dave Chinner
Quoted 17 lines · Stefan Roesch
This adds the async buffered write support to XFS. For async buffered write requests, the request will return
-EAGAINif the ilock cannot be obtained immediately.Signed-off-by Stefan Roesch <[email protected]>--- fs/xfs/xfs_file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)fs/xfs/xfs_file.c +0
-0@@ -739,12 +739,14 @@ xfs_file_buffered_write(bool cleared_space = false;int iolock;Quoted 15 lines
fs/xfs/xfs_file.c xfs_file_buffered_write( · line 749
Quoted 12 lines
fs/xfs/xfs_file.c +6
-4-if (iocb->ki_flags &IOCB_NOWAIT ) -return -EOPNOTSUPP ; - write_retry: iolock =XFS_IOLOCK_EXCL ; -xfs_ilock (ip, iolock); +if (iocb->ki_flags &IOCB_NOWAIT ) { +if (!xfs_ilock_nowait (ip, iolock)) +return -EAGAIN ; + }else { +xfs_ilock (ip, iolock); + }
xfs_ilock_iocb().Quoted 2 lines
The helper
xfs_ilock_iocbcannot be used as it hardcoded to useiocb->ki_filpto get a pointer to thexfs_inode.And the problem with that is?
I mean, look at what
xfs_file_buffered_write()does to get thexfs_inode10 lines about that change:xfs_file_buffered_write(
struct kiocb *iocb,
structiov_iter*from){ struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; struct xfs_inode *ip = XFS_I(inode);Quoted 9 lines
In what cases does
file_inode(iocb->ki_filp)point to a different inode thaniocb->ki_filp->f_mapping->host? The dio write path assumes thatfile_inode(iocb->ki_filp)is correct, as do both the buffered and dio read paths.What makes the buffered write path special in that
file_inode(iocb->ki_filp)is not correctly set whilstiocb->ki_filp->f_mapping->hostis?In the function
xfs_file_buffered_write()the code calls the functionxfs_ilock(). Thexfs_inodepointer that is passed in isiocb->ki_filp->f_mapping->host. The one used inxfs_ilock_iocbiski_filp->f_inode.After getting the lock, the code in
xfs_file_buffered_writecalls the functionxfs_buffered_write_iomap_begin(). In this function the code callsxfs_ilock()forki_filp->f_inodein exclusive mode.
Quoted 8 lines
Quoted 4 lines
If I replace the first
xfs_ilock()call withxfs_ilock_iocb(), then it looks like I get a deadlock.Am I missing something?
Yes. They take different locks.
xfs_file_buffered_write()takes the IOLOCK,xfs_buffered_write_iomap_begin()takes the ILOCK....
Thanks for the clarification.
Quoted 8 lines
Quoted 7 lines
I can:
- replace the pointer to iocb with pointer toxfs_inodein the functionxfs_ilock_iocb()and also pass in the flags value as a parameter.
or
- create functionxfs_ilock_inode(), whichxfs_ilock_iocb()calls. The existing calls will not need to change, only thexfs_ilockinxfs_file_buffered_write()will usexfs_ilock_inode().
Quoted 5 lines
Quoted 2 lines · You're making this way more complex than it needs to be. As I said:
Quoted 2 lines
Regardless, if this is a problem, then just pass the XFS inode to
xfs_ilock_iocb()and this is a moot point.
The function xfs_ilock_iocb() is expecting a pointer to the data structure kiocb, not a pointer to xfs_inode. I don't see how that's possible without changing the signature of xfs_ilock_iocb().
Do you want to invoke xfs_ilock_nowait() directly()?
Quoted 3 lines
Cheers,
Dave.
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 13 lines · Stefan Roesch
Quoted 12 lines · Dave Chinner
Quoted 8 lines · Stefan Roesch
Quoted 1 line · Dave Chinner
On Thu, Apr 28, 2022 at 12:58:59PM -0700, Stefan Roesch wrote:
- replace the pointer to iocb with pointer to
xfs_inodein the functionxfs_ilock_iocb()and also pass in the flags value as a parameter.
or
- create functionxfs_ilock_inode(), whichxfs_ilock_iocb()calls. The existing calls will not need to change, only thexfs_ilockinxfs_file_buffered_write()will usexfs_ilock_inode().You're making this way more complex than it needs to be. As I said:
Quoted 7 lines
Quoted 3 lines
Quoted 2 lines
Quoted 2 lines
Regardless, if this is a problem, then just pass the XFS inode to
xfs_ilock_iocb()and this is a moot point.The function
xfs_ilock_iocb()is expecting a pointer to the data structure kiocb, not a pointer toxfs_inode. I don't see how that's possible without changing the signature ofxfs_ilock_iocb().
For the *third time*: pass the xfs_inode to xfs_ilock_iocb() and update all the callers to do the same thing.
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 18 lines · Dave Chinner
Quoted 17 lines · Stefan Roesch
Quoted 15 lines · Dave Chinner
Quoted 8 lines · Stefan Roesch
Quoted 1 line · Dave Chinner
On Thu, Apr 28, 2022 at 12:58:59PM -0700, Stefan Roesch wrote:
- replace the pointer to iocb with pointer to
xfs_inodein the functionxfs_ilock_iocb()and also pass in the flags value as a parameter.
or
- create functionxfs_ilock_inode(), whichxfs_ilock_iocb()calls. The existing calls will not need to change, only thexfs_ilockinxfs_file_buffered_write()will usexfs_ilock_inode().Quoted 2 lines · You're making this way more complex than it needs to be. As I said:
Quoted 2 lines
Regardless, if this is a problem, then just pass the XFS inode to
xfs_ilock_iocb()and this is a moot point.
Quoted 6 lines
Quoted 3 lines
The function
xfs_ilock_iocb()is expecting a pointer to the data structure kiocb, not a pointer toxfs_inode. I don't see how that's possible without changing the signature ofxfs_ilock_iocb().For the *third time*: pass the
xfs_inodetoxfs_ilock_iocb()and update all the callers to do the same thing.
I still don't understand why /any/ of this is necessary. When does iocb->ki_filp->f_inode != iocb->ki_filp->f_mapping->host?
--D
Quoted 4 lines
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 23 lines · Darrick J. Wong
Quoted 22 lines · Dave Chinner
Quoted 20 lines · Stefan Roesch
Quoted 10 lines · Dave Chinner
Quoted 8 lines · Stefan Roesch
Quoted 1 line · Dave Chinner
On Thu, Apr 28, 2022 at 12:58:59PM -0700, Stefan Roesch wrote:
- replace the pointer to iocb with pointer to
xfs_inodein the functionxfs_ilock_iocb()and also pass in the flags value as a parameter.
or
- create functionxfs_ilock_inode(), whichxfs_ilock_iocb()calls. The existing calls will not need to change, only thexfs_ilockinxfs_file_buffered_write()will usexfs_ilock_inode().Quoted 5 lines
Quoted 2 lines · You're making this way more complex than it needs to be. As I said:
Quoted 2 lines
Regardless, if this is a problem, then just pass the XFS inode to
xfs_ilock_iocb()and this is a moot point.The function
xfs_ilock_iocb()is expecting a pointer to the data structure kiocb, not a pointer toxfs_inode. I don't see how that's possible without changing the signature ofxfs_ilock_iocb().
Quoted 5 lines
Quoted 2 lines
For the *third time*: pass the
xfs_inodetoxfs_ilock_iocb()and update all the callers to do the same thing.I still don't understand why /any/ of this is necessary. When does
iocb->ki_filp->f_inode!=iocb->ki_filp->f_mapping->host?
I already asked that question because I don't know the answer, either. I suspect the answer is "block dev inodes" but that then just raises the question of "how do we get them here?" and I don't know the answer to that, either. I don't have the time to dig into this and I don't expect anyone to just pop up with an answer, either. So in the mean time, we can just ignore it for the purpose of this patch set...
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 10 lines · Dave Chinner
Quoted 2 lines
I still don't understand why /any/ of this is necessary. When does
iocb->ki_filp->f_inode!=iocb->ki_filp->f_mapping->host?I already asked that question because I don't know the answer, either. I suspect the answer is "block dev inodes" but that then just raises the question of "how do we get them here?" and I don't know the answer to that, either. I don't have the time to dig into this and I don't expect anyone to just pop up with an answer, either. So in the mean time, we can just ignore it for the purpose of this patch set...
Weird device nodes (including block device) is the answer. It never happens for a normal file system file struct that we'd see in XFS.
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 12 lines · Christoph Hellwig
Quoted 10 lines · Dave Chinner
Quoted 2 lines
I still don't understand why /any/ of this is necessary. When does
iocb->ki_filp->f_inode!=iocb->ki_filp->f_mapping->host?I already asked that question because I don't know the answer, either. I suspect the answer is "block dev inodes" but that then just raises the question of "how do we get them here?" and I don't know the answer to that, either. I don't have the time to dig into this and I don't expect anyone to just pop up with an answer, either. So in the mean time, we can just ignore it for the purpose of this patch set...
Quoted 2 lines
Weird device nodes (including block device) is the answer. It never happens for a normal file system file struct that we'd see in XFS.
Ok, so we can just use XFS_I(file_inode(iocb->ki_filp)) then and we don't need to pass the xfs_inode at all. We probably should convert the rest of the io path to do this as well so we don't end up forgetting this again...
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [RFC PATCH v1 11/18] xfs: add async buffered write support
Quoted 4 lines · Dave Chinner
Ok, so we can just use XFS_I(file_inode(iocb->ki_filp)) then and we don't need to pass the
xfs_inodeat all. We probably should convert the rest of the io path to do this as well so we don't end up forgetting this again...
Yes.
RFC PATCH 12/18 io_uring: add support for async buffered writes
In reply to Stefan RoeschThis enables the async buffered writes for the filesystems that support async buffered writes in io-uring. Buffered writes are enabled for blocks that are already in the page cache or can be acquired with noio.
--- fs/io_uring.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)
fs/io_uring.c +24 -5
@@ -3746,7 +3746,7 @@ static inline int io_iter_do_read(struct io_kiocb *req, struct iov_iter *iter)return -EINVAL ; } -static bool need_read_all (struct io_kiocb *req) +static bool need_complete_io (struct io_kiocb *req) {return req->flags &REQ_F_ISREG ||S_ISBLK (file_inode (req->file)->i_mode);
@@ -3874,7 +3874,7 @@ static int io_read(struct io_kiocb *req, unsigned int issue_flags) }else if (ret == -EIOCBQUEUED ) { goto out_free; }else if (ret == req->result || ret <=0 || !force_nonblock || - (req->flags &REQ_F_NOWAIT ) || !need_read_all (req)) { + (req->flags &REQ_F_NOWAIT ) || !need_complete_io (req)) {/* read all, failed, already did sync or don't want to retry */ goto done; }
@@ -3970,9 +3970,10 @@ static int io_write(struct io_kiocb *req, unsigned int issue_flags)if (unlikely (!io_file_supports_nowait (req))) goto copy_iov; -/* file path doesn't support NOWAIT for non-direct_IO */ -if (force_nonblock && !(kiocb->ki_flags &IOCB_DIRECT ) && - (req->flags &REQ_F_ISREG )) +/* File path supports NOWAIT for non-direct_IO only for block devices. */ +if (!(kiocb->ki_flags &IOCB_DIRECT ) && + !(kiocb->ki_filp->f_mode &FMODE_BUF_WASYNC ) && + (req->flags &REQ_F_ISREG )) goto copy_iov; kiocb->ki_flags |=IOCB_NOWAIT ;
@@ -4026,6 +4027,24 @@ static int io_write(struct io_kiocb *req, unsigned int issue_flags)/* IOPOLL retry should happen for io-wq threads */ if (ret2 == -EAGAIN && (req->ctx->flags &IORING_SETUP_IOPOLL )) goto copy_iov; + +if (ret2 != req->result && ret2 >=0 &&need_complete_io (req)) { +struct io_async_rw *rw; + +/* This is a partial write. The file pos has already been +* updated, setup the async struct to complete the request +* in the worker. Also update bytes_done to account for +* the bytes already written. +*/ +iov_iter_save_state (&s->iter, &s->iter_state); + ret =io_setup_async_rw (req, iovec, s, true); + + rw = req->async_data; +if (rw) + rw->bytes_done += ret2; + +return ret ? ret : -EAGAIN ; + } done:kiocb_done (req, ret2, issue_flags); }else {
Signature · 1 line
2.30.2
RFC PATCH 13/18 io_uring: add tracepoint for short writes
In reply to Stefan RoeschThis adds the io_uring_short_write tracepoint to io_uring. A short write is issued if not all pages that are required for a write are in the page cache and the async buffered writes have to return EAGAIN.
--- fs/io_uring.c | 3 +++ include/trace/events/io_uring.h | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+)
fs/io_uring.c +3 -0
@@ -4031,6 +4031,9 @@ static int io_write(struct io_kiocb *req, unsigned int issue_flags)if (ret2 != req->result && ret2 >=0 &&need_complete_io (req)) {struct io_async_rw *rw; +trace_io_uring_short_write (req->ctx, kiocb->ki_pos - ret2, + req->result, ret2); +/* This is a partial write. The file pos has already been * updated, setup the async struct to complete the request * in the worker. Also update bytes_done to account for
include/trace/events/io_uring.h +25 -0
@@ -543,6 +543,31 @@ TRACE_EVENT(io_uring_req_failed, (unsigned long long) __entry->pad2, __entry->error) ); +TRACE_EVENT (io_uring_short_write, + + TP_PROTO(void *ctx, u64 fpos, u64 wanted, u64 got), + +TP_ARGS (ctx, fpos, wanted, got), + +TP_STRUCT__entry ( + __field(void *, ctx) + __field(u64, fpos) + __field(u64, wanted) +__field (u64, got) + ), + +TP_fast_assign ( + __entry->ctx = ctx; + __entry->fpos = fpos; + __entry->wanted = wanted; + __entry->got = got; + ), + +TP_printk ("ring %p, fpos %lld, wanted %lld, got %lld" , + __entry->ctx, __entry->fpos, + __entry->wanted, __entry->got) +); +#endif /* _TRACE_IO_URING_H */ /* This part must be outside protection */
Signature · 1 line
2.30.2
RFC PATCH 14/18 sched: add new fields to task_struct
In reply to Stefan RoeschAdd two new fields to the task_struct to support async write throttling.
- One field to store how long writes are throttled: bdp_pause
- The other field to store the number of dirtied pages:
bdp_nr_dirtied_pause
--- include/linux/sched.h | 3 +++ kernel/fork.c | 1 + 2 files changed, 4 insertions(+)
include/linux/sched.h +3 -0
@@ -1327,6 +1327,9 @@ struct task_struct {/* Start of a write-and-pause period: */ unsigned long dirty_paused_when; +unsigned long bdp_pause; +int bdp_nr_dirtied_pause; +#ifdef CONFIG_LATENCYTOP int latency_record_count;struct latency_record latency_record[LT_SAVECOUNT ];
kernel/fork.c +1 -0
@@ -2316,6 +2316,7 @@ static __latent_entropy struct task_struct *copy_process( p->nr_dirtied =0 ; p->nr_dirtied_pause =128 >> (PAGE_SHIFT -10 ); p->dirty_paused_when =0 ; + p->bdp_nr_dirtied_pause =-1 ; p->pdeath_signal =0 ;INIT_LIST_HEAD (&p->thread_group);
Signature · 1 line
2.30.2
RFC PATCH 15/18 mm: support write throttling for async buffered writes
In reply to Stefan RoeschThis change adds support for async write throttling in the function balance_dirty_pages(). So far if throttling was required, the code was waiting synchronously as long as the writes were throttled. This change introduces asynchronous throttling. Instead of waiting in the function balance_dirty_pages(), the timeout is set in the task_struct field bdp_pause. Once the timeout has expired, the writes are no longer throttled.
- Add a new parameter to the balance_dirty_pages() function
- This allows the caller to pass in the nowait flag
- When the nowait flag is specified, the code does not wait in balance_dirty_pages(), but instead stores the wait expiration in the new task_struct field bdp_pause.
- The function balance_dirty_pages_ratelimited() resets the new values in the task_struct, once the timeout has expired
This change is required to support write throttling for the async buffered writes. While the writes are throttled, io_uring still can make progress with processing other requests.
--- include/linux/writeback.h | 1 + mm/page-writeback.c | 54 ++++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 15 deletions(-)
include/linux/writeback.h +1 -0
@@ -373,6 +373,7 @@ unsigned long wb_calc_thresh(struct bdi_writeback *wb, unsigned long thresh);void wb_update_bandwidth (struct bdi_writeback *wb);void balance_dirty_pages_ratelimited (struct address_space *mapping); +void balance_dirty_pages_ratelimited_flags (struct address_space *mapping,bool is_async);bool wb_over_bg_thresh (struct bdi_writeback *wb); typedef int (*writepage_t)(struct page *page, struct writeback_control *wbc,
mm/page-writeback.c +39 -15
93 lines not on this page · raw message
Signature · 1 line
2.30.2
Jan Kara
maintainerRe: [RFC PATCH v1 15/18] mm: support write throttling for async buffered writes
Quoted 14 lines · Stefan Roesch
This change adds support for async write throttling in the function
balance_dirty_pages(). So far if throttling was required, the code was waiting synchronously as long as the writes were throttled. This change introduces asynchronous throttling. Instead of waiting in the functionbalance_dirty_pages(), the timeout is set in thetask_structfieldbdp_pause. Once the timeout has expired, the writes are no longer throttled.- Add a new parameter to the
balance_dirty_pages()function
- This allows the caller to pass in the nowait flag
- When the nowait flag is specified, the code does not wait inbalance_dirty_pages(), but instead stores the wait expiration in the newtask_structfieldbdp_pause.
Quoted 8 lines
- The function
balance_dirty_pages_ratelimited()resets the new values in thetask_struct, once the timeout has expiredThis change is required to support write throttling for the async buffered writes. While the writes are throttled,
io_uringstill can make progress with processing other requests.Signed-off-by Stefan Roesch <[email protected]>
Maybe I miss something but I don't think this will throttle writers enough. For three reasons:
1) The calculated throttling pauses should accumulate for the task so that if we compute that say it takes 0.1s to write 100 pages and the task writes 300 pages, the delay adds up to 0.3s properly. Otherwise the task would not be throttled as long as we expect the writeback to take.
2) We must not allow the amount of dirty pages to exceed the dirty limit. That can easily lead to page reclaim getting into trouble reclaiming pages and thus machine stalls, oom kills etc. So if we are coming close to dirty limit and we cannot sleep, we must just fail the nowait write.
3) Even with above two problems fixed I suspect results will be suboptimal because balance_dirty_pages() heuristics assume they get called reasonably often and throttle writes so if amount of dirty pages is coming close to dirty limit, they think we are overestimating writeback speed and update throttling parameters accordingly. So if io_uring code does not throttle writers often enough, I think dirty throttling parameters will be jumping wildly resulting in poor behavior.
So what I'd probably suggest is that if balance_dirty_pages() is called in "async" mode, we'd give tasks a pass until dirty_freerun_ceiling(). If balance_dirty_pages() decides the task needs to wait, we store the pause and bail all the way up into the place where we can sleep (io_uring code I assume), sleep there, and then continue doing write.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [RFC PATCH v1 15/18] mm: support write throttling for async buffered writes
Quoted 45 lines · Jan Kara
Quoted 14 lines · Stefan Roesch
This change adds support for async write throttling in the function
balance_dirty_pages(). So far if throttling was required, the code was waiting synchronously as long as the writes were throttled. This change introduces asynchronous throttling. Instead of waiting in the functionbalance_dirty_pages(), the timeout is set in thetask_structfieldbdp_pause. Once the timeout has expired, the writes are no longer throttled.- Add a new parameter to the
balance_dirty_pages()function
- This allows the caller to pass in the nowait flag
- When the nowait flag is specified, the code does not wait inbalance_dirty_pages(), but instead stores the wait expiration in the newtask_structfieldbdp_pause.Quoted 8 lines
- The function
balance_dirty_pages_ratelimited()resets the new values in thetask_struct, once the timeout has expiredThis change is required to support write throttling for the async buffered writes. While the writes are throttled,
io_uringstill can make progress with processing other requests.Signed-off-by Stefan Roesch <[email protected]>Maybe I miss something but I don't think this will throttle writers enough. For three reasons:
1) The calculated throttling pauses should accumulate for the task so that if we compute that say it takes 0.1s to write 100 pages and the task writes 300 pages, the delay adds up to 0.3s properly. Otherwise the task would not be throttled as long as we expect the writeback to take.
2) We must not allow the amount of dirty pages to exceed the dirty limit. That can easily lead to page reclaim getting into trouble reclaiming pages and thus machine stalls, oom kills etc. So if we are coming close to dirty limit and we cannot sleep, we must just fail the nowait write.
3) Even with above two problems fixed I suspect results will be suboptimal because
balance_dirty_pages()heuristics assume they get called reasonably often and throttle writes so if amount of dirty pages is coming close to dirty limit, they think we are overestimating writeback speed and update throttling parameters accordingly. So ifio_uringcode does not throttle writers often enough, I think dirty throttling parameters will be jumping wildly resulting in poor behavior.
Quoted 6 lines
So what I'd probably suggest is that if
balance_dirty_pages()is called in "async" mode, we'd give tasks a pass untildirty_freerun_ceiling(). Ifbalance_dirty_pages()decides the task needs to wait, we store the pause and bail all the way up into the place where we can sleep (io_uringcode I assume), sleep there, and then continue doing write.
Jan, thanks for the feedback. Are you suggesting to change the following check in the function balance_dirty_pages():
/*
* Throttle it only when the background writeback cannot
* catch-up. This avoids (excessively) small writeouts
* when the wb limits are ramping up in case of !strictlimit.
*
* In strictlimit case make decision based on the wb counters
* and limits. Small writeouts when the wb limits are ramping
* up are the price we consciously pay for strictlimit-ing.
*
* If memcg domain is in effect, @dirty should be under
* both global and memcg freerun ceilings.
*/
if (dirty <= dirty_freerun_ceiling(thresh, bg_thresh) &&
(!mdtc ||
m_dirty <= dirty_freerun_ceiling(m_thresh, m_bg_thresh))) {
unsigned long intv;
unsigned long m_intv;to include if we are in async mode?
There is no direct way to return that the process should sleep. Instead two new fields are introduced in the proc structure. These two fields are then used in io_uring to determine if the writes for a task need to be throttled.
In case the writes need to be throttled, the writes are not issued, but instead inserted on a wait queue. We cannot sleep in the general io_uring code path as we still want to process other requests which are affected by the throttling.
Quoted 2 lines
Honza
Jan Kara
maintainerRe: [RFC PATCH v1 15/18] mm: support write throttling for async buffered writes
Sorry for delayed reply. This has fallen through the cracks...
Quoted 74 lines · Stefan Roesch
Quoted 45 lines · Jan Kara
Quoted 14 lines · Stefan Roesch
This change adds support for async write throttling in the function
balance_dirty_pages(). So far if throttling was required, the code was waiting synchronously as long as the writes were throttled. This change introduces asynchronous throttling. Instead of waiting in the functionbalance_dirty_pages(), the timeout is set in thetask_structfieldbdp_pause. Once the timeout has expired, the writes are no longer throttled.- Add a new parameter to the
balance_dirty_pages()function
- This allows the caller to pass in the nowait flag
- When the nowait flag is specified, the code does not wait inbalance_dirty_pages(), but instead stores the wait expiration in the newtask_structfieldbdp_pause.Quoted 8 lines
- The function
balance_dirty_pages_ratelimited()resets the new values in thetask_struct, once the timeout has expiredThis change is required to support write throttling for the async buffered writes. While the writes are throttled,
io_uringstill can make progress with processing other requests.Signed-off-by Stefan Roesch <[email protected]>Maybe I miss something but I don't think this will throttle writers enough. For three reasons:
1) The calculated throttling pauses should accumulate for the task so that if we compute that say it takes 0.1s to write 100 pages and the task writes 300 pages, the delay adds up to 0.3s properly. Otherwise the task would not be throttled as long as we expect the writeback to take.
2) We must not allow the amount of dirty pages to exceed the dirty limit. That can easily lead to page reclaim getting into trouble reclaiming pages and thus machine stalls, oom kills etc. So if we are coming close to dirty limit and we cannot sleep, we must just fail the nowait write.
3) Even with above two problems fixed I suspect results will be suboptimal because
balance_dirty_pages()heuristics assume they get called reasonably often and throttle writes so if amount of dirty pages is coming close to dirty limit, they think we are overestimating writeback speed and update throttling parameters accordingly. So ifio_uringcode does not throttle writers often enough, I think dirty throttling parameters will be jumping wildly resulting in poor behavior.Quoted 6 lines
So what I'd probably suggest is that if
balance_dirty_pages()is called in "async" mode, we'd give tasks a pass untildirty_freerun_ceiling(). Ifbalance_dirty_pages()decides the task needs to wait, we store the pause and bail all the way up into the place where we can sleep (io_uringcode I assume), sleep there, and then continue doing write.Jan, thanks for the feedback. Are you suggesting to change the following check in the function
balance_dirty_pages():/* * Throttle it only when the background writeback cannot * catch-up. This avoids (excessively) small writeouts * when the wb limits are ramping up in case of !strictlimit. * * In strictlimit case make decision based on the wb counters * and limits. Small writeouts when the wb limits are ramping * up are the price we consciously pay for strictlimit-ing. * * If memcg domain is in effect, @dirty should be under * both global and memcg freerun ceilings. */ if (dirty <= dirty_freerun_ceiling(thresh, bg_thresh) && (!mdtc || m_dirty <= dirty_freerun_ceiling(m_thresh, m_bg_thresh))) { unsigned long intv; unsigned long m_intv;
Quoted 1 line
to include if we are in async mode?
Actually no. This condition is the one that gives any task a free pass until dirty_freerun_ceiling(). So there's no need to do any modification for that. Sorry, I've probably formulated my suggestion in a bit confusing way.
Quoted 9 lines
There is no direct way to return that the process should sleep. Instead two new fields are introduced in the proc structure. These two fields are then used in
io_uringto determine if the writes for a task need to be throttled.In case the writes need to be throttled, the writes are not issued, but instead inserted on a wait queue. We cannot sleep in the general
io_uringcode path as we still want to process other requests which are affected by the throttling.
Probably you wanted to say "are not affected by the throttling" in the above.
I know that you're using fields in task_struct to propagate the delay info. But IMHO that is unnecessary (although I don't care too much). Instead we could factor out a variant of balance_dirty_pages() that returns 'pause' to sleep, 0 if no sleeping needed. Normal balance_dirty_pages() would use this for pause calculation, places wanting async throttling would only get the pause to sleep. So e.g. iomap_write_iter() would then check and if returned pause is > 0, it would abort the loop similary as we'd abort it for any other reason when NOWAIT write is aborted because we need to sleep. Iouring code then detects short write / EAGAIN and offloads the write to the workqueue where normal balance_dirty_pages() can sleep as needed.
This will make sure dirty limits are properly observed and we don't need that much special handling for it.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [RFC PATCH v1 15/18] mm: support write throttling for async buffered writes
Quoted 89 lines · Jan Kara
Sorry for delayed reply. This has fallen through the cracks...
Quoted 74 lines · Stefan Roesch
Quoted 45 lines · Jan Kara
Quoted 14 lines · Stefan Roesch
This change adds support for async write throttling in the function
balance_dirty_pages(). So far if throttling was required, the code was waiting synchronously as long as the writes were throttled. This change introduces asynchronous throttling. Instead of waiting in the functionbalance_dirty_pages(), the timeout is set in thetask_structfieldbdp_pause. Once the timeout has expired, the writes are no longer throttled.- Add a new parameter to the
balance_dirty_pages()function
- This allows the caller to pass in the nowait flag
- When the nowait flag is specified, the code does not wait inbalance_dirty_pages(), but instead stores the wait expiration in the newtask_structfieldbdp_pause.Quoted 8 lines
- The function
balance_dirty_pages_ratelimited()resets the new values in thetask_struct, once the timeout has expiredThis change is required to support write throttling for the async buffered writes. While the writes are throttled,
io_uringstill can make progress with processing other requests.Signed-off-by Stefan Roesch <[email protected]>Maybe I miss something but I don't think this will throttle writers enough. For three reasons:
1) The calculated throttling pauses should accumulate for the task so that if we compute that say it takes 0.1s to write 100 pages and the task writes 300 pages, the delay adds up to 0.3s properly. Otherwise the task would not be throttled as long as we expect the writeback to take.
2) We must not allow the amount of dirty pages to exceed the dirty limit. That can easily lead to page reclaim getting into trouble reclaiming pages and thus machine stalls, oom kills etc. So if we are coming close to dirty limit and we cannot sleep, we must just fail the nowait write.
3) Even with above two problems fixed I suspect results will be suboptimal because
balance_dirty_pages()heuristics assume they get called reasonably often and throttle writes so if amount of dirty pages is coming close to dirty limit, they think we are overestimating writeback speed and update throttling parameters accordingly. So ifio_uringcode does not throttle writers often enough, I think dirty throttling parameters will be jumping wildly resulting in poor behavior.Quoted 6 lines
So what I'd probably suggest is that if
balance_dirty_pages()is called in "async" mode, we'd give tasks a pass untildirty_freerun_ceiling(). Ifbalance_dirty_pages()decides the task needs to wait, we store the pause and bail all the way up into the place where we can sleep (io_uringcode I assume), sleep there, and then continue doing write.Jan, thanks for the feedback. Are you suggesting to change the following check in the function
balance_dirty_pages():/* * Throttle it only when the background writeback cannot * catch-up. This avoids (excessively) small writeouts * when the wb limits are ramping up in case of !strictlimit. * * In strictlimit case make decision based on the wb counters * and limits. Small writeouts when the wb limits are ramping * up are the price we consciously pay for strictlimit-ing. * * If memcg domain is in effect, @dirty should be under * both global and memcg freerun ceilings. */ if (dirty <= dirty_freerun_ceiling(thresh, bg_thresh) && (!mdtc || m_dirty <= dirty_freerun_ceiling(m_thresh, m_bg_thresh))) { unsigned long intv; unsigned long m_intv;Quoted 1 line
to include if we are in async mode?
Actually no. This condition is the one that gives any task a free pass until
dirty_freerun_ceiling(). So there's no need to do any modification for that. Sorry, I've probably formulated my suggestion in a bit confusing way.Quoted 5 lines
There is no direct way to return that the process should sleep. Instead two new fields are introduced in the proc structure. These two fields are then used in
io_uringto determine if the writes for a task need to be throttled.
Quoted 8 lines
Quoted 4 lines
In case the writes need to be throttled, the writes are not issued, but instead inserted on a wait queue. We cannot sleep in the general
io_uringcode path as we still want to process other requests which are affected by the throttling.Probably you wanted to say "are not affected by the throttling" in the above.
Yes, that's correct.
Quoted 11 lines
I know that you're using fields in
task_structto propagate the delay info. But IMHO that is unnecessary (although I don't care too much). Instead we could factor out a variant ofbalance_dirty_pages()that returns 'pause' to sleep, 0 if no sleeping needed. Normalbalance_dirty_pages()would use this for pause calculation, places wanting async throttling would only get the pause to sleep. So e.g.iomap_write_iter()would then check and if returned pause is > 0, it would abort the loop similary as we'd abort it for any other reason when NOWAIT write is aborted because we need to sleep. Iouring code then detects short write / EAGAIN and offloads the write to the workqueue where normalbalance_dirty_pages()can sleep as needed.
Quoted 3 lines
This will make sure dirty limits are properly observed and we don't need that much special handling for it.
I like the idea of factoring out a function out balance_dirty_pages(), however
I see two challenges:
- the write operation has already completed at this point,
- so we can't really sleep on its completion in the io-worker in io-uring
- we don't know how long to sleep in io-uring
Currently balance_dirty_pages_ratelimited() is called at the end of the function iomap_write_iter(). If the function balance_dirty_pages_ratelimited() would instead be called at the beginning of the function iomap_write_iter() we could return -EAGAIN and then complete it in the io-worker.
I'm not sure what the implications are of moving the function call to the beginning of the function iomap_write_iter().
Quoted 1 line
Honza
Jan Kara
maintainerRe: [RFC PATCH v1 15/18] mm: support write throttling for async buffered writes
Quoted 23 lines · Stefan Roesch
Quoted 11 lines · Jan Kara
I know that you're using fields in
task_structto propagate the delay info. But IMHO that is unnecessary (although I don't care too much). Instead we could factor out a variant ofbalance_dirty_pages()that returns 'pause' to sleep, 0 if no sleeping needed. Normalbalance_dirty_pages()would use this for pause calculation, places wanting async throttling would only get the pause to sleep. So e.g.iomap_write_iter()would then check and if returned pause is > 0, it would abort the loop similary as we'd abort it for any other reason when NOWAIT write is aborted because we need to sleep. Iouring code then detects short write / EAGAIN and offloads the write to the workqueue where normalbalance_dirty_pages()can sleep as needed.Quoted 3 lines
This will make sure dirty limits are properly observed and we don't need that much special handling for it.
I like the idea of factoring out a function out
balance_dirty_pages(), howeverI see two challenges:
- the write operation has already completed at this point,
- so we can't really sleep on its completion in the io-worker in io-uring
- we don't know how long to sleep in io-uring
Quoted 5 lines
Currently
balance_dirty_pages_ratelimited()is called at the end of the functioniomap_write_iter(). If the functionbalance_dirty_pages_ratelimited()would instead be called at the beginning of the functioniomap_write_iter()we could return-EAGAINand then complete it in the io-worker.
Well, we call balance_dirty_pages_ratelimited() after each page. So it does not really matter much if the sleep is pushed to happen one page later. balance_dirty_pages_ratelimited() does ratelimiting of when balance_dirty_pages() are called so we have to make sure current->nr_dirtied is not zeroed out before we really do wait (because that is what determines whether we enter balance_dirty_pages() and how long we sleep there) but looking at the code that should work out just fine.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [RFC PATCH v1 15/18] mm: support write throttling for async buffered writes
Quoted 30 lines · Jan Kara
Quoted 23 lines · Stefan Roesch
Quoted 11 lines · Jan Kara
I know that you're using fields in
task_structto propagate the delay info. But IMHO that is unnecessary (although I don't care too much). Instead we could factor out a variant ofbalance_dirty_pages()that returns 'pause' to sleep, 0 if no sleeping needed. Normalbalance_dirty_pages()would use this for pause calculation, places wanting async throttling would only get the pause to sleep. So e.g.iomap_write_iter()would then check and if returned pause is > 0, it would abort the loop similary as we'd abort it for any other reason when NOWAIT write is aborted because we need to sleep. Iouring code then detects short write / EAGAIN and offloads the write to the workqueue where normalbalance_dirty_pages()can sleep as needed.Quoted 3 lines
This will make sure dirty limits are properly observed and we don't need that much special handling for it.
I like the idea of factoring out a function out
balance_dirty_pages(), howeverI see two challenges:
- the write operation has already completed at this point,
- so we can't really sleep on its completion in the io-worker in io-uring
- we don't know how long to sleep in io-uringQuoted 5 lines
Currently
balance_dirty_pages_ratelimited()is called at the end of the functioniomap_write_iter(). If the functionbalance_dirty_pages_ratelimited()would instead be called at the beginning of the functioniomap_write_iter()we could return-EAGAINand then complete it in the io-worker.
Quoted 8 lines
Well, we call
balance_dirty_pages_ratelimited()after each page. So it does not really matter much if the sleep is pushed to happen one page later.balance_dirty_pages_ratelimited()does ratelimiting of whenbalance_dirty_pages()are called so we have to make surecurrent->nr_dirtiedis not zeroed out before we really do wait (because that is what determines whether we enterbalance_dirty_pages()and how long we sleep there) but looking at the code that should work out just fine.
I'll make the changes to balance_dirty_pages() for the next version of the patch series.
Quoted 1 line
Honza
RFC PATCH 16/18 iomap: User throttling for async buffered writes.
In reply to Stefan RoeschThis enables throttling for async buffered writes.
--- fs/iomap/buffered-io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/iomap/buffered-io.c +2 -1
@@ -843,7 +843,8 @@ static loff_t iomap_write_iter(struct iomap_iter *iter, struct iov_iter *i) written += status; length -= status; -balance_dirty_pages_ratelimited (iter->inode->i_mapping); +balance_dirty_pages_ratelimited_flags (iter->inode->i_mapping, + (iter->flags &IOMAP_NOWAIT )); }while (iov_iter_count (i) && length);return written ? written : status;
Signature · 1 line
2.30.2
RFC PATCH 17/18 io_uring: support write throttling for async buffered writes
In reply to Stefan RoeschThis adds the process-level throttling for the block layer for async buffered writes to io-uring. In io_write the code now checks if the write needs to be throttled. If this is required, it adds the request to the list of pending io requests and starts a timer. After the timer expires, it submits the list of pending writes.
- Add new list called pending_ios for delayed writes (throttled writes) to struct io_uring_task. The list is protected by the task_lock spin lock.
- Add new timer to struct io_uring_task.
--- fs/io_uring.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 7 deletions(-)
fs/io_uring.c +92 -7
@@ -485,6 +485,11 @@ struct io_ring_ctx {*/ #define IO_RINGFD_REG_MAX 16 +struct pending_list { +struct list_head list; +struct io_kiocb *req; +}; +struct io_uring_task {/* submission side */ int cached_refs;
@@ -501,6 +506,9 @@ struct io_uring_task {struct callback_head task_work;struct file **registered_rings;bool task_running; + +struct pending_list pending_ios; +struct timer_list timer; };/*
@@ -1193,7 +1201,7 @@ static void io_rsrc_put_work(struct work_struct *work);static void io_req_task_queue (struct io_kiocb *req);static void __io_submit_flush_completions (struct io_ring_ctx *ctx); -static int io_req_prep_async (struct io_kiocb *req); +static int io_req_prep_async (struct io_kiocb *req,bool force);static int io_install_fixed_file (struct io_kiocb *req,struct file *file,unsigned int issue_flags,u32 slot_index);
@@ -1201,6 +1209,7 @@ static int io_close_fixed(struct io_kiocb *req, unsigned int issue_flags);static enum hrtimer_restart io_link_timeout_fn (struct hrtimer *timer);static void io_eventfd_signal (struct io_ring_ctx *ctx); +static void delayed_write_fn (struct timer_list *tmr);static struct kmem_cache *req_cachep;
@@ -2599,6 +2608,32 @@ static void io_req_task_queue_reissue(struct io_kiocb *req)io_req_task_work_add (req, false); } +static int io_req_task_queue_reissue_delayed (struct io_kiocb *req) +{ +struct io_uring_task *tctx = req->task->io_uring; +struct pending_list *pending; +bool empty; + + pending =kmalloc (sizeof (struct pending_list ),GFP_KERNEL ); +if (!pending) +return -ENOMEM ; + pending->req = req; + +spin_lock_irq (&tctx->task_lock); + empty =list_empty (&tctx->pending_ios.list); +list_add_tail (&pending->list, &tctx->pending_ios.list); + +if (empty) { +timer_setup (&tctx->timer, delayed_write_fn,0 ); + + tctx->timer.expires = current->bdp_pause; +add_timer (&tctx->timer); + } +spin_unlock_irq (&tctx->task_lock); + +return 0 ; +} +static inline void io_queue_next (struct io_kiocb *req) {struct io_kiocb *nxt =io_req_find_next (req);
@@ -2916,7 +2951,7 @@ static bool io_resubmit_prep(struct io_kiocb *req)struct io_async_rw *rw = req->async_data;if (!req_has_async_data (req)) -return !io_req_prep_async (req); +return !io_req_prep_async (req, false);iov_iter_restore (&rw->s.iter, &rw->s.iter_state);return true; }
@@ -3938,6 +3973,38 @@ static int io_read(struct io_kiocb *req, unsigned int issue_flags)return 0 ; } +static inline unsigned long write_delay (void ) +{ +if (likely (current->bdp_nr_dirtied_pause ==-1 || + !time_before (jiffies, current->bdp_pause))) +return 0 ; + +return current->bdp_pause; +} + +static void delayed_write_fn (struct timer_list *tmr) +{ +struct io_uring_task *tctx =from_timer (tctx, tmr, timer); +struct list_head *curr; +struct list_head *next; +LIST_HEAD (pending_ios); + +/* Move list to temporary list. */ +spin_lock_irq (&tctx->task_lock); +list_splice_init (&tctx->pending_ios.list, &pending_ios); +spin_unlock_irq (&tctx->task_lock); + +list_for_each_safe (curr, next, &pending_ios) { +struct pending_list *io; + + io = list_entry(curr, struct pending_list, list); +io_req_task_queue_reissue (io->req); + +list_del (curr); +kfree (io); + } +} +static int io_write (struct io_kiocb *req,unsigned int issue_flags) {struct io_rw_state __s, *s = &__s;
@@ -3947,6 +4014,18 @@ static int io_write(struct io_kiocb *req, unsigned int issue_flags)ssize_t ret, ret2;loff_t *ppos; +/* Write throttling active? */ +if (!(kiocb->ki_flags &IOCB_DIRECT ) &&unlikely (write_delay ())) { +int ret =io_req_prep_async (req, true); + +if (unlikely (ret)) +io_req_complete_failed (req, ret); +else + ret =io_req_task_queue_reissue_delayed (req); + +return ret; + } +if (!req_has_async_data (req)) { ret =io_import_iovec (WRITE , req, &iovec, s, issue_flags);if (unlikely (ret <0 ))
@@ -6962,9 +7041,9 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)return -EINVAL ; } -static int io_req_prep_async (struct io_kiocb *req) +static int io_req_prep_async (struct io_kiocb *req,bool force) { -if (!io_op_defs[req->opcode].needs_async_setup) +if (!force && !io_op_defs[req->opcode].needs_async_setup)return 0 ;if (WARN_ON_ONCE (req_has_async_data (req)))return -EFAULT ;
@@ -6974,6 +7053,10 @@ static int io_req_prep_async(struct io_kiocb *req)switch (req->opcode) {case IORING_OP_READV :return io_rw_prep_async (req,READ ); +case IORING_OP_WRITE : +if (!force) +break ; + fallthrough;case IORING_OP_WRITEV :return io_rw_prep_async (req,WRITE );case IORING_OP_SENDMSG :
@@ -6983,6 +7066,7 @@ static int io_req_prep_async(struct io_kiocb *req)case IORING_OP_CONNECT :return io_connect_prep_async (req); } +printk_once (KERN_WARNING "io_uring: prep_async() bad opcode %d\n" , req->opcode);return -EFAULT ;
@@ -7016,7 +7100,7 @@ static __cold void io_drain_req(struct io_kiocb *req) }spin_unlock (&ctx->completion_lock); - ret =io_req_prep_async (req); + ret =io_req_prep_async (req, false);if (ret) { fail:io_req_complete_failed (req, ret);
@@ -7550,7 +7634,7 @@ static void io_queue_sqe_fallback(struct io_kiocb *req) }else if (unlikely (req->ctx->drain_active)) {io_drain_req (req); }else { -int ret =io_req_prep_async (req); +int ret =io_req_prep_async (req, false);if (unlikely (ret))io_req_complete_failed (req, ret);
@@ -7746,7 +7830,7 @@ static int io_submit_sqe(struct io_ring_ctx *ctx, struct io_kiocb *req,struct io_kiocb *head = link->head;if (!(req->flags &REQ_F_FAIL )) { - ret =io_req_prep_async (req); + ret =io_req_prep_async (req, false);if (unlikely (ret)) {req_fail_link_node (req, ret);if (!(head->flags &REQ_F_FAIL ))
@@ -9222,6 +9306,7 @@ static __cold int io_uring_alloc_task_context(struct task_struct *task,INIT_WQ_LIST (&tctx->task_list);INIT_WQ_LIST (&tctx->prior_task_list);init_task_work (&tctx->task_work, tctx_task_work); +INIT_LIST_HEAD (&tctx->pending_ios.list);return 0 ; }
Signature · 1 line
2.30.2
RFC PATCH 18/18 xfs: enable async buffered write support
In reply to Stefan RoeschThis turns on the async buffered write support for XFS.
--- fs/xfs/xfs_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_file.c +1 -1
@@ -1210,7 +1210,7 @@ xfs_file_open(return -EFBIG ;if (xfs_is_shutdown (XFS_M (inode->i_sb)))return -EIO ; - file->f_mode |=FMODE_NOWAIT |FMODE_BUF_RASYNC ; + file->f_mode |=FMODE_NOWAIT |FMODE_BUF_RASYNC |FMODE_BUF_WASYNC ;return 0 ; }
Signature · 1 line
2.30.2
Quoted 18 lines · Stefan Roesch
This patch series adds support for async buffered writes when using both xfs and io-uring. Currently io-uring only supports buffered writes in the slow path, by processing them in the io workers. With this patch series it is now possible to support buffered writes in the fast path. To be able to use the fast path the required pages must be in the page cache, the required locks in xfs can be granted immediately and no additional blocks need to be read form disk.
Updating the inode can take time. An optimization has been implemented for the time update. Time updates will be processed in the slow path. While there is already a time update in process, other write requests for the same file, can skip the update of the modification time.
Performance results:
For fio the following results have been obtained with a queue depth of 1 and 4k block size (runtime 600 secs):
Quoted 8 lines
sequential writes:
without patch with patch
iops: 80k 269krandom writes:
without patch with patch
iops: 76k 249k
What's the results when you enable the lazytime mount option to elide almost all the timestamp updates from buffered writes?
As it is, on my test setup:
# fio --readwrite=write --filesize=5g --filename=/mnt/scratch/foo --runtime=30s --time_based --ioengine=libaio --direct=0 --bs=4k --iodepth=1 --numjobs=1 -name=/mnt/scratch/foobar --group_reporting --fallocate=none ..... write: IOPS=296k, BW=1157MiB/s (1213MB/s)(33.9GiB/30001msec); 0 zone resets
So buffered IO is getting ~295k IOPS on a default kernel config. Using --ioengine=psync gets ~325k IOPS. However, using --ioengine=io_uring I only get:
.....
write: IOPS=58.5k, BW=229MiB/s (240MB/s)(6861MiB/30001msec); 0 zone resets
~60k IOPS. IOWs, normal buffered IO is 5x faster than the io_uring slow path.
To summarise:
seq IOPS rand IOPS
libaio ~295k ~220k
psync ~325k ~255kio_uring ~60k ~45k
IOWs, there's nothing wrong with the normal blocking IO path and it easily reaches full single CPU performance on this machine. However, there is a massive increase in overhead in using the io_uring slow path.
Oh, wow, an average of 5 context switches per buffered write IO with io_uring? There's zero context switches with psync or libaio. That blocking path overhead seems like a general candidate for io_uring optimisation, especially as most buffered writes are going to require blocking for one reason or another regardless of whether we add a NOWAIT fast path or not...
Quoted 7 lines
For an io depth of 1, the new patch improves throughput by over three times (compared to the exiting behavior, where buffered writes are processed by an io-worker process) and also the latency is considerably reduced. To achieve the same or better performance with the exisiting code an io depth of 4 is required. Increasing the iodepth further does not lead to any further improvements.
Especially for mixed workloads this is a considerable improvement.
Improving the slow path so it's only half as slow as the existing psync/libaio IO paths would also be a major improvement for mixed workloads...
Quoted 10 lines
Support for async buffered writes:
Patch 1: block: add check for async buffered writes to
generic_write_checks
Add a new flagFMODE_BUF_WASYNCso filesystems can specify that they support async buffered writes and include the flag in the check of the functiongeneric_write_checks().Patch 2: mm: add
FGP_ATOMICflag to__filemap_get_folio()
This adds theFGP_ATOMICflag. This allows to specify the gfp flags for memory allocations for async buffered writes.
I haven't even looked at the patches yet, but this gets a hard NACK. Unbound GFP_ATOMIC allocations in a user controlled context is a total non-starter.
Quoted 14 lines
Patch 3: iomap: add
iomap_page_create_gfpto allocateiomap_pages
Add new function to allow specifying gfp flags when allocating and initializing the structureiomap_page.Patch 4: iomap: use
iomap_page_create_gfp()in__iomap_write_begin
Add a gfp flag to theiomap_page_createfunction.Patch 5: iomap: add async buffered write support
SetIOMAP_NOWAITflag ifIOCB_NOWAITis set. Also use specific gfp flags if theiomap_pagestructure is allocated for an async buffered page.Patch 6: xfs: add iomap async buffered write support
Add async buffered write support to the xfs iomap layer.
Quoted 6 lines
Support for async buffered write support and inode time modification
Patch 7: fs: split off
need_remove_file_privs()do_remove_file_privs()
Splits of a check and action function, so they can later be invoked in the nowait code path.
I don't like the sound of that. Separating the stripping of SUID/SGID bits and other important things from the check opens a potential window between the check and action where state changes can be made that the check/action haven't taken into account...
This seems like a dangerous architectural direction to be headed as it introduces an uncloseable race window where things can change between checks and actions. Especially as it's likely easily controllable from userspace because of the locking involved in filesystem modification operations....
Quoted 7 lines
Patch 8: fs: split off
need_file_update_timeanddo_file_update_time
Splits of a check and action function, so they can later be invoked in the nowait code path.Patch 9: fs: add pending file update time flag.
Add new flag so consecutive write requests for the same inode can proceed without waiting for the inode modification time to complete.
Isn't this exactly what the lazytime mount option already gives us?
Quoted 21 lines
Patch 10: xfs: enable async write file modification handling.
Enable async write handling in xfs for the file modification time update. If the file modification update requires logging or removal of privileges that needs to wait,-EAGAINis returned.Patch 11: xfs: add async buffered write support
Take the ilock in nowait mode if async buffered writes are enabled.Patch 12:
io_uring: add support for async buffered writes
This enables the async buffered writes optimization inio_uring. Buffered writes are enabled for blocks that are already in the page cache.Patch 13:
io_uring: Add tracepoint for short writesSupport for write throttling of async buffered writes:
Patch 14: sched: add new fields to
task_struct
Add two new fields to thetask_struct. These fields store the deadline after which writes are no longer throttled.
Quoted 6 lines
Patch 15: mm: support write throttling for async buffered writes
This changes thebalance_dirty_pagesfunction to take an additional parameter. When nowait is specified the write throttling code no longer waits synchronously for the deadline to expire. Instead it sets the fields intask_struct. Once the deadline expires the fields are reset.
How have you tested this? How does it interact with non io-uring writers to files on the same fs/backing device? How does it interact with other devices when they are at full writeback speed and throttling writes on the dirty limits, too?
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]