summaryrefslogtreecommitdiff
path: root/fs/fs-writeback.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fs-writeback.c')
-rw-r--r--fs/fs-writeback.c268
1 files changed, 197 insertions, 71 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 7c75ed7e8979..e744f9f9d43f 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -25,7 +25,7 @@
#include <linux/pagemap.h>
#include <linux/kthread.h>
#include <linux/writeback.h>
-#include <linux/blkdev.h>
+#include <linux/blk_plug.h>
#include <linux/backing-dev.h>
#include <linux/tracepoint.h>
#include <linux/device.h>
@@ -280,15 +280,13 @@ void __inode_attach_wb(struct inode *inode, struct folio *folio)
if (inode_cgwb_enabled(inode)) {
struct cgroup_subsys_state *memcg_css;
- if (folio) {
- memcg_css = mem_cgroup_css_from_folio(folio);
- wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC);
- } else {
- /* must pin memcg_css, see wb_get_create() */
+ /* must pin memcg_css, see wb_get_create() */
+ if (folio)
+ memcg_css = get_mem_cgroup_css_from_folio(folio);
+ else
memcg_css = task_get_css(current, memory_cgrp_id);
- wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC);
- css_put(memcg_css);
- }
+ wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC);
+ css_put(memcg_css);
}
if (!wb)
@@ -301,6 +299,7 @@ void __inode_attach_wb(struct inode *inode, struct folio *folio)
if (unlikely(cmpxchg(&inode->i_wb, NULL, wb)))
wb_put(wb);
}
+EXPORT_SYMBOL_GPL(__inode_attach_wb);
/**
* inode_cgwb_move_to_attached - put the inode onto wb->b_attached list
@@ -434,6 +433,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);
+ }
}
}
@@ -499,6 +502,23 @@ skip_switch:
return switched;
}
+static inline void cgroup_writeback_pin(struct super_block *sb)
+{
+ atomic_inc(&sb->s_isw_nr_in_flight);
+}
+
+static inline void cgroup_writeback_unpin(struct super_block *sb)
+{
+ if (atomic_dec_and_test(&sb->s_isw_nr_in_flight))
+ wake_up_var(&sb->s_isw_nr_in_flight);
+}
+
+static inline void cgroup_writeback_drain(struct super_block *sb)
+{
+ wait_var_event(&sb->s_isw_nr_in_flight,
+ !atomic_read(&sb->s_isw_nr_in_flight));
+}
+
static void process_inode_switch_wbs(struct bdi_writeback *new_wb,
struct inode_switch_wbs_context *isw)
{
@@ -556,8 +576,12 @@ relock:
wb_put_many(old_wb, nr_switched);
}
- for (inodep = isw->inodes; *inodep; inodep++)
+ for (inodep = isw->inodes; *inodep; inodep++) {
+ struct super_block *sb = (*inodep)->i_sb;
+
iput(*inodep);
+ cgroup_writeback_unpin(sb);
+ }
wb_put(new_wb);
kfree(isw);
atomic_dec(&isw_nr_in_flight);
@@ -570,44 +594,49 @@ void inode_switch_wbs_work_fn(struct work_struct *work)
struct inode_switch_wbs_context *isw, *next_isw;
struct llist_node *list;
+ list = llist_del_all(&new_wb->switch_wbs_ctxs);
/*
- * Grab out reference to wb so that it cannot get freed under us
+ * Nothing to do? That would be a problem as references held by isw
+ * items protect wb from freeing...
+ */
+ if (WARN_ON_ONCE(!list))
+ return;
+
+ /*
+ * Grab our reference to wb so that it cannot get freed under us
* after we process all the isw items.
*/
wb_get(new_wb);
- while (1) {
- list = llist_del_all(&new_wb->switch_wbs_ctxs);
- /* Nothing to do? */
- if (!list)
- break;
- /*
- * In addition to synchronizing among switchers, I_WB_SWITCH
- * tells the RCU protected stat update paths to grab the i_page
- * lock so that stat transfer can synchronize against them.
- * Let's continue after I_WB_SWITCH is guaranteed to be
- * visible.
- */
- synchronize_rcu();
+ /*
+ * In addition to synchronizing among switchers, I_WB_SWITCH
+ * tells the RCU protected stat update paths to grab the i_page
+ * lock so that stat transfer can synchronize against them.
+ * Let's continue after I_WB_SWITCH is guaranteed to be
+ * visible.
+ */
+ synchronize_rcu();
- llist_for_each_entry_safe(isw, next_isw, list, list)
- process_inode_switch_wbs(new_wb, isw);
- }
+ llist_for_each_entry_safe(isw, next_isw, list, list)
+ process_inode_switch_wbs(new_wb, isw);
wb_put(new_wb);
}
static bool inode_prepare_wbs_switch(struct inode *inode,
struct bdi_writeback *new_wb)
{
+ /* Avoid the atomic_inc/smp_mb dance once SB_ACTIVE is gone. */
+ if (!(inode->i_sb->s_flags & SB_ACTIVE))
+ return false;
+
/*
- * Paired with smp_mb() in cgroup_writeback_umount().
- * isw_nr_in_flight must be increased before checking SB_ACTIVE and
- * grabbing an inode, otherwise isw_nr_in_flight can be observed as 0
- * in cgroup_writeback_umount() and the isw_wq will be not flushed.
+ * Pairs with smp_mb() in cgroup_writeback_umount(): the umounter either
+ * sees a non-zero counter and waits, or we see SB_ACTIVE clear below.
*/
+ cgroup_writeback_pin(inode->i_sb);
smp_mb();
if (IS_DAX(inode))
- return false;
+ goto out_unpin;
/* while holding I_WB_SWITCH, no one else can update the association */
spin_lock(&inode->i_lock);
@@ -615,13 +644,17 @@ static bool inode_prepare_wbs_switch(struct inode *inode,
inode_state_read(inode) & (I_WB_SWITCH | I_FREEING | I_WILL_FREE) ||
inode_to_wb(inode) == new_wb) {
spin_unlock(&inode->i_lock);
- return false;
+ goto out_unpin;
}
inode_state_set(inode, I_WB_SWITCH);
__iget(inode);
spin_unlock(&inode->i_lock);
return true;
+
+out_unpin:
+ cgroup_writeback_unpin(inode->i_sb);
+ return false;
}
static void wb_queue_isw(struct bdi_writeback *wb,
@@ -979,16 +1012,16 @@ void wbc_account_cgroup_owner(struct writeback_control *wbc, struct folio *folio
if (!wbc->wb || wbc->no_cgroup_owner)
return;
- css = mem_cgroup_css_from_folio(folio);
+ css = get_mem_cgroup_css_from_folio(folio);
/* dead cgroups shouldn't contribute to inode ownership arbitration */
if (!css_is_online(css))
- return;
+ goto out;
id = css->id;
if (id == wbc->wb_id) {
wbc->wb_bytes += bytes;
- return;
+ goto out;
}
if (id == wbc->wb_lcand_id)
@@ -1001,6 +1034,8 @@ void wbc_account_cgroup_owner(struct writeback_control *wbc, struct folio *folio
wbc->wb_tcand_bytes += bytes;
else
wbc->wb_tcand_bytes -= min(bytes, wbc->wb_tcand_bytes);
+out:
+ css_put(css);
}
EXPORT_SYMBOL_GPL(wbc_account_cgroup_owner);
@@ -1196,36 +1231,27 @@ out_bdi_put:
}
/**
- * cgroup_writeback_umount - flush inode wb switches for umount
+ * cgroup_writeback_umount - wait for in-flight inode wb switches on @sb
* @sb: target super_block
*
- * This function is called when a super_block is about to be destroyed and
- * flushes in-flight inode wb switches. An inode wb switch goes through
- * RCU and then workqueue, so the two need to be flushed in order to ensure
- * that all previously scheduled switches are finished. As wb switches are
- * rare occurrences and synchronize_rcu() can take a while, perform
- * flushing iff wb switches are in flight.
+ * Wait until every inode wb switch that already passed the SB_ACTIVE
+ * check on this superblock has been completed by the worker. Since
+ * SB_ACTIVE is cleared before this is called, no new switches can start
+ * for @sb, so s_isw_nr_in_flight will monotonically drop to zero.
*/
void cgroup_writeback_umount(struct super_block *sb)
{
-
if (!(sb->s_bdi->capabilities & BDI_CAP_WRITEBACK))
return;
/*
- * SB_ACTIVE should be reliably cleared before checking
- * isw_nr_in_flight, see generic_shutdown_super().
+ * Pairs with smp_mb() in inode_prepare_wbs_switch(): we either observe
+ * a non-zero counter and wait, or the switcher sees SB_ACTIVE clear
+ * (cleared by generic_shutdown_super()) and bails before grabbing the
+ * inode.
*/
smp_mb();
-
- if (atomic_read(&isw_nr_in_flight)) {
- /*
- * Use rcu_barrier() to wait for all pending callbacks to
- * ensure that all in-flight wb switches are in the workqueue.
- */
- rcu_barrier();
- flush_workqueue(isw_wq);
- }
+ cgroup_writeback_drain(sb);
}
static int __init cgroup_writeback_init(void)
@@ -1711,6 +1737,19 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
}
}
+static bool __sync_lazytime(struct inode *inode)
+{
+ spin_lock(&inode->i_lock);
+ if (!(inode_state_read(inode) & I_DIRTY_TIME)) {
+ spin_unlock(&inode->i_lock);
+ return false;
+ }
+ inode_state_clear(inode, I_DIRTY_TIME);
+ spin_unlock(&inode->i_lock);
+ inode->i_op->sync_lazytime(inode);
+ return true;
+}
+
bool sync_lazytime(struct inode *inode)
{
if (!(inode_state_read_once(inode) & I_DIRTY_TIME))
@@ -1718,9 +1757,8 @@ bool sync_lazytime(struct inode *inode)
trace_writeback_lazytime(inode);
if (inode->i_op->sync_lazytime)
- inode->i_op->sync_lazytime(inode);
- else
- mark_inode_dirty_sync(inode);
+ return __sync_lazytime(inode);
+ mark_inode_dirty_sync(inode);
return true;
}
@@ -1814,6 +1852,22 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
if (ret == 0)
ret = err;
}
+
+ /*
+ * Do we need to wait for inode metadata IO possibly submitted
+ * by previous WB_SYNC_NONE writeback?
+ */
+ if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync &&
+ inode_state_read_once(inode) & I_METADATA_WRITEBACK) {
+ int err;
+
+ spin_lock(&inode->i_lock);
+ inode_state_clear(inode, I_METADATA_WRITEBACK);
+ spin_unlock(&inode->i_lock);
+ err = inode->i_sb->s_op->sync_inode_metadata(inode, wbc);
+ if (ret == 0)
+ ret = err;
+ }
wbc->unpinned_netfs_wb = false;
trace_writeback_single_inode(inode, wbc, nr_to_write);
return ret;
@@ -1855,14 +1909,17 @@ static int writeback_single_inode(struct inode *inode,
/*
* If the inode is already fully clean, then there's nothing to do.
*
- * For data-integrity syncs we also need to check whether any pages are
- * still under writeback, e.g. due to prior WB_SYNC_NONE writeback. If
- * there are any such pages, we'll need to wait for them.
+ * For data-integrity syncs we also need to check whether any folios or
+ * metadata are still under writeback, e.g. due to prior WB_SYNC_NONE
+ * writeback. If there, we'll need to wait for them.
*/
- if (!(inode_state_read(inode) & I_DIRTY_ALL) &&
- (wbc->sync_mode != WB_SYNC_ALL ||
- !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK)))
- goto out;
+ if (!(inode_state_read(inode) & I_DIRTY_ALL)) {
+ if (wbc->sync_mode != WB_SYNC_ALL)
+ goto out;
+ if (!mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK) &&
+ !(inode_state_read(inode) & I_METADATA_WRITEBACK))
+ goto out;
+ }
inode_state_set(inode, I_SYNC);
wbc_attach_and_unlock_inode(wbc, inode);
@@ -2359,6 +2416,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
@@ -2381,6 +2459,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
*/
wrote += wb_check_old_data_flush(wb);
@@ -2454,6 +2537,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
*/
@@ -2775,13 +2895,8 @@ static void wait_sb_inodes(struct super_block *sb)
* The mapping can appear untagged while still on-list since we
* do not have the mapping lock. Skip it here, wb completion
* will remove it.
- *
- * If the mapping does not have data integrity semantics,
- * there's no need to wait for the writeout to complete, as the
- * mapping cannot guarantee that data is persistently stored.
*/
- if (!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK) ||
- mapping_no_data_integrity(mapping))
+ if (!mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK))
continue;
spin_unlock_irq(&sb->s_inode_wblist_lock);
@@ -2916,6 +3031,17 @@ void sync_inodes_sb(struct super_block *sb)
*/
if (bdi == &noop_backing_dev_info)
return;
+
+ /*
+ * If the superblock has SB_I_NO_DATA_INTEGRITY set, there's no need to
+ * wait for the writeout to complete, as the filesystem cannot guarantee
+ * data persistence on sync. Just kick off writeback and return.
+ */
+ if (sb->s_iflags & SB_I_NO_DATA_INTEGRITY) {
+ wakeup_flusher_threads_bdi(bdi, WB_REASON_SYNC);
+ return;
+ }
+
WARN_ON(!rwsem_is_locked(&sb->s_umount));
/* protect against inode wb switch, see inode_switch_wbs_work_fn() */