Skip to content

Commit 6b1fd22

Browse files
joannekoongbrauner
authored andcommitted
iomap: optimize pending async writeback accounting
Pending writebacks must be accounted for to determine when all requests have completed and writeback on the folio should be ended. Currently this is done by atomically incrementing ifs->write_bytes_pending for every range to be written back. Instead, the number of atomic operations can be minimized by setting ifs->write_bytes_pending to the folio size, internally tracking how many bytes are written back asynchronously, and then after sending off all the requests, decrementing ifs->write_bytes_pending by the number of bytes not written back asynchronously. Now, for N ranges written back, only N + 2 atomic operations are required instead of 2N + 2. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Link: https://patch.msgid.link/20251111193658.3495942-5-joannelkoong@gmail.com Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 7e6cea5 commit 6b1fd22

4 files changed

Lines changed: 36 additions & 30 deletions

File tree

fs/fuse/file.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,7 +1885,8 @@ static void fuse_writepage_finish(struct fuse_writepage_args *wpa)
18851885
* scope of the fi->lock alleviates xarray lock
18861886
* contention and noticeably improves performance.
18871887
*/
1888-
iomap_finish_folio_write(inode, ap->folios[i], 1);
1888+
iomap_finish_folio_write(inode, ap->folios[i],
1889+
ap->descs[i].length);
18891890

18901891
wake_up(&fi->page_waitq);
18911892
}
@@ -2221,7 +2222,6 @@ static ssize_t fuse_iomap_writeback_range(struct iomap_writepage_ctx *wpc,
22212222
ap = &wpa->ia.ap;
22222223
}
22232224

2224-
iomap_start_folio_write(inode, folio, 1);
22252225
fuse_writepage_args_page_fill(wpa, folio, ap->num_folios,
22262226
offset, len);
22272227
data->nr_bytes += len;

fs/iomap/buffered-io.c

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,16 +1641,25 @@ vm_fault_t iomap_page_mkwrite(struct vm_fault *vmf, const struct iomap_ops *ops,
16411641
}
16421642
EXPORT_SYMBOL_GPL(iomap_page_mkwrite);
16431643

1644-
void iomap_start_folio_write(struct inode *inode, struct folio *folio,
1645-
size_t len)
1644+
static void iomap_writeback_init(struct inode *inode, struct folio *folio)
16461645
{
16471646
struct iomap_folio_state *ifs = folio->private;
16481647

16491648
WARN_ON_ONCE(i_blocks_per_folio(inode, folio) > 1 && !ifs);
1650-
if (ifs)
1651-
atomic_add(len, &ifs->write_bytes_pending);
1649+
if (ifs) {
1650+
WARN_ON_ONCE(atomic_read(&ifs->write_bytes_pending) != 0);
1651+
/*
1652+
* Set this to the folio size. After processing the folio for
1653+
* writeback in iomap_writeback_folio(), we'll subtract any
1654+
* ranges not written back.
1655+
*
1656+
* We do this because otherwise, we would have to atomically
1657+
* increment ifs->write_bytes_pending every time a range in the
1658+
* folio needs to be written back.
1659+
*/
1660+
atomic_set(&ifs->write_bytes_pending, folio_size(folio));
1661+
}
16521662
}
1653-
EXPORT_SYMBOL_GPL(iomap_start_folio_write);
16541663

16551664
void iomap_finish_folio_write(struct inode *inode, struct folio *folio,
16561665
size_t len)
@@ -1667,7 +1676,7 @@ EXPORT_SYMBOL_GPL(iomap_finish_folio_write);
16671676

16681677
static int iomap_writeback_range(struct iomap_writepage_ctx *wpc,
16691678
struct folio *folio, u64 pos, u32 rlen, u64 end_pos,
1670-
bool *wb_pending)
1679+
size_t *bytes_submitted)
16711680
{
16721681
do {
16731682
ssize_t ret;
@@ -1681,11 +1690,11 @@ static int iomap_writeback_range(struct iomap_writepage_ctx *wpc,
16811690
pos += ret;
16821691

16831692
/*
1684-
* Holes are not be written back by ->writeback_range, so track
1693+
* Holes are not written back by ->writeback_range, so track
16851694
* if we did handle anything that is not a hole here.
16861695
*/
16871696
if (wpc->iomap.type != IOMAP_HOLE)
1688-
*wb_pending = true;
1697+
*bytes_submitted += ret;
16891698
} while (rlen);
16901699

16911700
return 0;
@@ -1756,7 +1765,7 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio)
17561765
u64 pos = folio_pos(folio);
17571766
u64 end_pos = pos + folio_size(folio);
17581767
u64 end_aligned = 0;
1759-
bool wb_pending = false;
1768+
size_t bytes_submitted = 0;
17601769
int error = 0;
17611770
u32 rlen;
17621771

@@ -1776,14 +1785,7 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio)
17761785
iomap_set_range_dirty(folio, 0, end_pos - pos);
17771786
}
17781787

1779-
/*
1780-
* Keep the I/O completion handler from clearing the writeback
1781-
* bit until we have submitted all blocks by adding a bias to
1782-
* ifs->write_bytes_pending, which is dropped after submitting
1783-
* all blocks.
1784-
*/
1785-
WARN_ON_ONCE(atomic_read(&ifs->write_bytes_pending) != 0);
1786-
iomap_start_folio_write(inode, folio, 1);
1788+
iomap_writeback_init(inode, folio);
17871789
}
17881790

17891791
/*
@@ -1798,13 +1800,13 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio)
17981800
end_aligned = round_up(end_pos, i_blocksize(inode));
17991801
while ((rlen = iomap_find_dirty_range(folio, &pos, end_aligned))) {
18001802
error = iomap_writeback_range(wpc, folio, pos, rlen, end_pos,
1801-
&wb_pending);
1803+
&bytes_submitted);
18021804
if (error)
18031805
break;
18041806
pos += rlen;
18051807
}
18061808

1807-
if (wb_pending)
1809+
if (bytes_submitted)
18081810
wpc->nr_folios++;
18091811

18101812
/*
@@ -1822,12 +1824,20 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio)
18221824
* bit ourselves right after unlocking the page.
18231825
*/
18241826
if (ifs) {
1825-
if (atomic_dec_and_test(&ifs->write_bytes_pending))
1826-
folio_end_writeback(folio);
1827-
} else {
1828-
if (!wb_pending)
1829-
folio_end_writeback(folio);
1827+
/*
1828+
* Subtract any bytes that were initially accounted to
1829+
* write_bytes_pending but skipped for writeback.
1830+
*/
1831+
size_t bytes_not_submitted = folio_size(folio) -
1832+
bytes_submitted;
1833+
1834+
if (bytes_not_submitted)
1835+
iomap_finish_folio_write(inode, folio,
1836+
bytes_not_submitted);
1837+
} else if (!bytes_submitted) {
1838+
folio_end_writeback(folio);
18301839
}
1840+
18311841
mapping_set_error(inode->i_mapping, error);
18321842
return error;
18331843
}

fs/iomap/ioend.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,6 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio,
194194
if (!bio_add_folio(&ioend->io_bio, folio, map_len, poff))
195195
goto new_ioend;
196196

197-
iomap_start_folio_write(wpc->inode, folio, map_len);
198-
199197
/*
200198
* Clamp io_offset and io_size to the incore EOF so that ondisk
201199
* file size updates in the ioend completion are byte-accurate.

include/linux/iomap.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,6 @@ int iomap_ioend_writeback_submit(struct iomap_writepage_ctx *wpc, int error);
478478

479479
void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len,
480480
int error);
481-
void iomap_start_folio_write(struct inode *inode, struct folio *folio,
482-
size_t len);
483481
void iomap_finish_folio_write(struct inode *inode, struct folio *folio,
484482
size_t len);
485483

0 commit comments

Comments
 (0)