Merge patch series "mm: improve write performance with RWF_DONTCACHE"
Jeff Layton <jlayton@kernel.org> says: This patch series is intended to improve write performance with RWF_DONTCACHE. This version fixes additional stat accounting issues found during review: integer promotion on 32-bit, cgroup writeback domain migration, folio split flag preservation, and a UAF that could occur in filemap_dontcache_kick_writeback(). * patches from https://patch.msgid.link/20260511-dontcache-v7-0-2848ddce8090@kernel.org: mm: kick writeback flusher for IOCB_DONTCACHE with targeted dirty tracking mm: track DONTCACHE dirty pages per bdi_writeback mm: preserve PG_dropbehind flag during folio split Link: https://patch.msgid.link/20260511-dontcache-v7-0-2848ddce8090@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
7 files changed
+94
-7
No files matched your search
@@ -432,6 +432,10 @@ static bool inode_do_switch_wbs(struct inode *inode,
|
||||
long nr = folio_nr_pages(folio);
|
||||
wb_stat_mod(old_wb, WB_RECLAIMABLE, -nr);
|
||||
wb_stat_mod(new_wb, WB_RECLAIMABLE, nr);
|
||||
if (folio_test_dropbehind(folio)) {
|
||||
wb_stat_mod(old_wb, WB_DONTCACHE_DIRTY, -nr);
|
||||
wb_stat_mod(new_wb, WB_DONTCACHE_DIRTY, nr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2392,6 +2396,27 @@ static long wb_check_start_all(struct bdi_writeback *wb)
|
||||
return nr_pages;
|
||||
}
|
||||
|
||||
static long wb_check_start_dontcache(struct bdi_writeback *wb)
|
||||
{
|
||||
long nr_pages;
|
||||
|
||||
if (!test_and_clear_bit(WB_start_dontcache, &wb->state))
|
||||
return 0;
|
||||
|
||||
nr_pages = wb_stat_sum(wb, WB_DONTCACHE_DIRTY);
|
||||
if (nr_pages) {
|
||||
struct wb_writeback_work work = {
|
||||
.nr_pages = nr_pages,
|
||||
.sync_mode = WB_SYNC_NONE,
|
||||
.range_cyclic = 1,
|
||||
.reason = WB_REASON_DONTCACHE,
|
||||
};
|
||||
|
||||
nr_pages = wb_writeback(wb, &work);
|
||||
}
|
||||
|
||||
return nr_pages;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve work items and do the writeback they describe
|
||||
@@ -2413,6 +2438,11 @@ static long wb_do_writeback(struct bdi_writeback *wb)
|
||||
*/
|
||||
wrote += wb_check_start_all(wb);
|
||||
|
||||
/*
|
||||
* Check for dontcache writeback request
|
||||
*/
|
||||
wrote += wb_check_start_dontcache(wb);
|
||||
|
||||
/*
|
||||
* Check for periodic writeback, kupdated() style
|
||||
*/
|
||||
@@ -2487,6 +2517,43 @@ void wakeup_flusher_threads_bdi(struct backing_dev_info *bdi,
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* filemap_dontcache_kick_writeback - kick flusher for IOCB_DONTCACHE writes
|
||||
* @mapping: address_space that was just written to
|
||||
*
|
||||
* Kick the writeback flusher thread to expedite writeback of dontcache dirty
|
||||
* pages. Queue writeback for the inode's wb for as many pages as there are
|
||||
* dontcache pages, but don't restrict writeback to dontcache pages only.
|
||||
*
|
||||
* This significantly improves performance over either writing all wb's pages
|
||||
* or writing only dontcache pages. Although it doesn't guarantee quick
|
||||
* writeback and reclaim of dontcache pages, it keeps the amount of dirty pages
|
||||
* in check. Over longer term dontcache pages get written and reclaimed by
|
||||
* background writeback even with this rough heuristic.
|
||||
*/
|
||||
void filemap_dontcache_kick_writeback(struct address_space *mapping)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct bdi_writeback *wb;
|
||||
struct wb_lock_cookie cookie = {};
|
||||
bool need_wakeup = false;
|
||||
|
||||
wb = unlocked_inode_to_wb_begin(inode, &cookie);
|
||||
if (wb_has_dirty_io(wb) &&
|
||||
!test_bit(WB_start_dontcache, &wb->state) &&
|
||||
!test_and_set_bit(WB_start_dontcache, &wb->state)) {
|
||||
wb_get(wb);
|
||||
need_wakeup = true;
|
||||
}
|
||||
unlocked_inode_to_wb_end(inode, &cookie);
|
||||
|
||||
if (need_wakeup) {
|
||||
wb_wakeup(wb);
|
||||
wb_put(wb);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(filemap_dontcache_kick_writeback);
|
||||
|
||||
/*
|
||||
* Wakeup the flusher threads to start writeback of all currently dirty pages
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,7 @@ enum wb_state {
|
||||
WB_writeback_running, /* Writeback is in progress */
|
||||
WB_has_dirty_io, /* Dirty inodes on ->b_{dirty|io|more_io} */
|
||||
WB_start_all, /* nr_pages == 0 (all) work pending */
|
||||
WB_start_dontcache, /* dontcache writeback pending */
|
||||
};
|
||||
|
||||
enum wb_stat_item {
|
||||
@@ -33,6 +34,7 @@ enum wb_stat_item {
|
||||
WB_WRITEBACK,
|
||||
WB_DIRTIED,
|
||||
WB_WRITTEN,
|
||||
WB_DONTCACHE_DIRTY,
|
||||
NR_WB_STAT_ITEMS
|
||||
};
|
||||
|
||||
@@ -55,6 +57,7 @@ enum wb_reason {
|
||||
*/
|
||||
WB_REASON_FORKER_THREAD,
|
||||
WB_REASON_FOREIGN_FLUSH,
|
||||
WB_REASON_DONTCACHE,
|
||||
|
||||
WB_REASON_MAX,
|
||||
};
|
||||
|
||||
+2
-4
@@ -2624,6 +2624,7 @@ extern int __must_check file_write_and_wait_range(struct file *file,
|
||||
loff_t start, loff_t end);
|
||||
int filemap_flush_range(struct address_space *mapping, loff_t start,
|
||||
loff_t end);
|
||||
void filemap_dontcache_kick_writeback(struct address_space *mapping);
|
||||
|
||||
static inline int file_write_and_wait(struct file *file)
|
||||
{
|
||||
@@ -2657,10 +2658,7 @@ static inline ssize_t generic_write_sync(struct kiocb *iocb, ssize_t count)
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (iocb->ki_flags & IOCB_DONTCACHE) {
|
||||
struct address_space *mapping = iocb->ki_filp->f_mapping;
|
||||
|
||||
filemap_flush_range(mapping, iocb->ki_pos - count,
|
||||
iocb->ki_pos - 1);
|
||||
filemap_dontcache_kick_writeback(iocb->ki_filp->f_mapping);
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
EM( WB_REASON_PERIODIC, "periodic") \
|
||||
EM( WB_REASON_FS_FREE_SPACE, "fs_free_space") \
|
||||
EM( WB_REASON_FORKER_THREAD, "forker_thread") \
|
||||
EMe(WB_REASON_FOREIGN_FLUSH, "foreign_flush")
|
||||
EM( WB_REASON_FOREIGN_FLUSH, "foreign_flush") \
|
||||
EMe(WB_REASON_DONTCACHE, "dontcache")
|
||||
|
||||
WB_WORK_REASON
|
||||
|
||||
|
||||
+13
-2
@@ -2052,8 +2052,19 @@ no_page:
|
||||
if (!folio)
|
||||
return ERR_PTR(-ENOENT);
|
||||
/* not an uncached lookup, clear uncached if set */
|
||||
if (folio_test_dropbehind(folio) && !(fgp_flags & FGP_DONTCACHE))
|
||||
folio_clear_dropbehind(folio);
|
||||
if (!(fgp_flags & FGP_DONTCACHE) && folio_test_clear_dropbehind(folio)) {
|
||||
if (folio_test_dirty(folio) &&
|
||||
mapping_can_writeback(mapping)) {
|
||||
struct inode *inode = mapping->host;
|
||||
struct bdi_writeback *wb;
|
||||
struct wb_lock_cookie cookie = {};
|
||||
long nr = folio_nr_pages(folio);
|
||||
|
||||
wb = unlocked_inode_to_wb_begin(inode, &cookie);
|
||||
wb_stat_mod(wb, WB_DONTCACHE_DIRTY, -nr);
|
||||
unlocked_inode_to_wb_end(inode, &cookie);
|
||||
}
|
||||
}
|
||||
return folio;
|
||||
}
|
||||
EXPORT_SYMBOL(__filemap_get_folio_mpol);
|
||||
|
||||
@@ -3642,6 +3642,7 @@ static void __split_folio_to_order(struct folio *folio, int old_order,
|
||||
(1L << PG_arch_3) |
|
||||
#endif
|
||||
(1L << PG_dirty) |
|
||||
(1L << PG_dropbehind) |
|
||||
LRU_GEN_MASK | LRU_REFS_MASK));
|
||||
|
||||
if (handle_hwpoison &&
|
||||
|
||||
@@ -2630,6 +2630,8 @@ static void folio_account_dirtied(struct folio *folio,
|
||||
wb = inode_to_wb(inode);
|
||||
|
||||
lruvec_stat_mod_folio(folio, NR_FILE_DIRTY, nr);
|
||||
if (folio_test_dropbehind(folio))
|
||||
wb_stat_mod(wb, WB_DONTCACHE_DIRTY, nr);
|
||||
__zone_stat_mod_folio(folio, NR_ZONE_WRITE_PENDING, nr);
|
||||
__node_stat_mod_folio(folio, NR_DIRTIED, nr);
|
||||
wb_stat_mod(wb, WB_RECLAIMABLE, nr);
|
||||
@@ -2651,6 +2653,8 @@ void folio_account_cleaned(struct folio *folio, struct bdi_writeback *wb)
|
||||
long nr = folio_nr_pages(folio);
|
||||
|
||||
lruvec_stat_mod_folio(folio, NR_FILE_DIRTY, -nr);
|
||||
if (folio_test_dropbehind(folio))
|
||||
wb_stat_mod(wb, WB_DONTCACHE_DIRTY, -nr);
|
||||
zone_stat_mod_folio(folio, NR_ZONE_WRITE_PENDING, -nr);
|
||||
wb_stat_mod(wb, WB_RECLAIMABLE, -nr);
|
||||
task_io_account_cancelled_write(nr * PAGE_SIZE);
|
||||
@@ -2920,6 +2924,8 @@ bool folio_clear_dirty_for_io(struct folio *folio)
|
||||
if (folio_test_clear_dirty(folio)) {
|
||||
long nr = folio_nr_pages(folio);
|
||||
lruvec_stat_mod_folio(folio, NR_FILE_DIRTY, -nr);
|
||||
if (folio_test_dropbehind(folio))
|
||||
wb_stat_mod(wb, WB_DONTCACHE_DIRTY, -nr);
|
||||
zone_stat_mod_folio(folio, NR_ZONE_WRITE_PENDING, -nr);
|
||||
wb_stat_mod(wb, WB_RECLAIMABLE, -nr);
|
||||
ret = true;
|
||||
|
||||
Reference in new issue
Block a user