From 4117efd5c9ecd9d1d531f85967df7a394d783cf2 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 19 Aug 2024 13:32:44 +0200 Subject: [PATCH 1/6] gfs2: Minor gfs2_glock_cb cleanup In gfs2_glock_cb(), we only need to calculate the glock hold time for inode glocks; the value is unused otherwise. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 12a769077ea0..4a026703a28d 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1885,14 +1885,16 @@ void gfs2_glock_dq_m(unsigned int num_gh, struct gfs2_holder *ghs) void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state) { unsigned long delay = 0; - unsigned long holdtime; - unsigned long now = jiffies; gfs2_glock_hold(gl); spin_lock(&gl->gl_lockref.lock); - holdtime = gl->gl_tchange + gl->gl_hold_time; if (!list_empty(&gl->gl_holders) && gl->gl_name.ln_type == LM_TYPE_INODE) { + unsigned long now = jiffies; + unsigned long holdtime; + + holdtime = gl->gl_tchange + gl->gl_hold_time; + if (time_before(now, holdtime)) delay = holdtime - now; if (test_bit(GLF_HAVE_REPLY, &gl->gl_flags)) From 6cb9df81a2c462b89d2f9611009ab43ae8717841 Mon Sep 17 00:00:00 2001 From: Julian Sun Date: Tue, 20 Aug 2024 11:31:48 +0800 Subject: [PATCH 2/6] gfs2: fix double destroy_workqueue error When gfs2_fill_super() fails, destroy_workqueue() is called within gfs2_gl_hash_clear(), and the subsequent code path calls destroy_workqueue() on the same work queue again. This issue can be fixed by setting the work queue pointer to NULL after the first destroy_workqueue() call and checking for a NULL pointer before attempting to destroy the work queue again. Reported-by: syzbot+d34c2a269ed512c531b0@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=d34c2a269ed512c531b0 Fixes: 30e388d57367 ("gfs2: Switch to a per-filesystem glock workqueue") Cc: stable@vger.kernel.org Signed-off-by: Julian Sun Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 1 + fs/gfs2/ops_fstype.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 4a026703a28d..269c3bc7fced 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -2251,6 +2251,7 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp) gfs2_free_dead_glocks(sdp); glock_hash_walk(dump_glock_func, sdp); destroy_workqueue(sdp->sd_glock_wq); + sdp->sd_glock_wq = NULL; } static const char *state2str(unsigned state) diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index ff1f3e3dc65c..e83d293c3614 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1307,7 +1307,8 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) fail_delete_wq: destroy_workqueue(sdp->sd_delete_wq); fail_glock_wq: - destroy_workqueue(sdp->sd_glock_wq); + if (sdp->sd_glock_wq) + destroy_workqueue(sdp->sd_glock_wq); fail_free: free_sbd(sdp); sb->s_fs_info = NULL; From 901849e7070b2b74ba1adaaf025a09a1f381fefe Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 19 Jul 2024 18:51:01 +0100 Subject: [PATCH 3/6] gfs2: Add gfs2_aspace_writepages() This saves one indirect function call per folio and gets us closer to removing aops->writepage. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andreas Gruenbacher --- fs/gfs2/meta_io.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index 2b26e8d529aa..cfb204c9396c 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -30,9 +30,9 @@ #include "util.h" #include "trace_gfs2.h" -static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wbc) +static void gfs2_aspace_write_folio(struct folio *folio, + struct writeback_control *wbc) { - struct folio *folio = page_folio(page); struct buffer_head *bh, *head; int nr_underway = 0; blk_opf_t write_flags = REQ_META | REQ_PRIO | wbc_to_write_flags(wbc); @@ -66,8 +66,8 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wb } while ((bh = bh->b_this_page) != head); /* - * The page and its buffers are protected by PageWriteback(), so we can - * drop the bh refcounts early. + * The folio and its buffers are protected from truncation by + * the writeback flag, so we can drop the bh refcounts early. */ BUG_ON(folio_test_writeback(folio)); folio_start_writeback(folio); @@ -84,14 +84,31 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wb if (nr_underway == 0) folio_end_writeback(folio); +} + +static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wbc) +{ + gfs2_aspace_write_folio(page_folio(page), wbc); return 0; } +static int gfs2_aspace_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct folio *folio = NULL; + int error; + + while ((folio = writeback_iter(mapping, wbc, folio, &error))) + gfs2_aspace_write_folio(folio, wbc); + + return error; +} + const struct address_space_operations gfs2_meta_aops = { .dirty_folio = block_dirty_folio, .invalidate_folio = block_invalidate_folio, - .writepage = gfs2_aspace_writepage, + .writepages = gfs2_aspace_writepages, .release_folio = gfs2_release_folio, }; @@ -99,6 +116,7 @@ const struct address_space_operations gfs2_rgrp_aops = { .dirty_folio = block_dirty_folio, .invalidate_folio = block_invalidate_folio, .writepage = gfs2_aspace_writepage, + .writepages = gfs2_aspace_writepages, .release_folio = gfs2_release_folio, }; From 8d391972ae2d4fcee9c928581dfb6cb4ce6a3938 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 19 Jul 2024 18:51:02 +0100 Subject: [PATCH 4/6] gfs2: Remove __gfs2_writepage() Call aops->writepages() instead of using write_cache_pages() to call aops->writepage. Change the handling of -ENODATA to not set the persistent error on the block device. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andreas Gruenbacher --- fs/gfs2/log.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 6ee6013fb825..f9c5089783d2 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -80,15 +80,6 @@ void gfs2_remove_from_ail(struct gfs2_bufdata *bd) brelse(bd->bd_bh); } -static int __gfs2_writepage(struct folio *folio, struct writeback_control *wbc, - void *data) -{ - struct address_space *mapping = data; - int ret = mapping->a_ops->writepage(&folio->page, wbc); - mapping_set_error(mapping, ret); - return ret; -} - /** * gfs2_ail1_start_one - Start I/O on a transaction * @sdp: The superblock @@ -140,7 +131,7 @@ __acquires(&sdp->sd_ail_lock) if (!mapping) continue; spin_unlock(&sdp->sd_ail_lock); - ret = write_cache_pages(mapping, wbc, __gfs2_writepage, mapping); + ret = mapping->a_ops->writepages(mapping, wbc); if (need_resched()) { blk_finish_plug(plug); cond_resched(); @@ -149,6 +140,7 @@ __acquires(&sdp->sd_ail_lock) spin_lock(&sdp->sd_ail_lock); if (ret == -ENODATA) /* if a jdata write into a new hole */ ret = 0; /* ignore it */ + mapping_set_error(mapping, ret); if (ret || wbc->nr_to_write <= 0) break; return -EBUSY; From e5ac17199275faffc5399ff7c8a0bea1db553f26 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 19 Jul 2024 18:51:03 +0100 Subject: [PATCH 5/6] gfs2: Remove gfs2_jdata_writepage() There are no remaining callers of gfs2_jdata_writepage() other than vmscan, which is known to do more harm than good. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andreas Gruenbacher --- fs/gfs2/aops.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 10d5acd3f742..68fc8af14700 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -138,35 +138,6 @@ static int __gfs2_jdata_write_folio(struct folio *folio, return gfs2_write_jdata_folio(folio, wbc); } -/** - * gfs2_jdata_writepage - Write complete page - * @page: Page to write - * @wbc: The writeback control - * - * Returns: errno - * - */ - -static int gfs2_jdata_writepage(struct page *page, struct writeback_control *wbc) -{ - struct folio *folio = page_folio(page); - struct inode *inode = page->mapping->host; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - - if (gfs2_assert_withdraw(sdp, ip->i_gl->gl_state == LM_ST_EXCLUSIVE)) - goto out; - if (folio_test_checked(folio) || current->journal_info) - goto out_ignore; - return __gfs2_jdata_write_folio(folio, wbc); - -out_ignore: - folio_redirty_for_writepage(wbc, folio); -out: - folio_unlock(folio); - return 0; -} - /** * gfs2_writepages - Write a bunch of dirty pages back to disk * @mapping: The mapping to write @@ -748,7 +719,6 @@ static const struct address_space_operations gfs2_aops = { }; static const struct address_space_operations gfs2_jdata_aops = { - .writepage = gfs2_jdata_writepage, .writepages = gfs2_jdata_writepages, .read_folio = gfs2_read_folio, .readahead = gfs2_readahead, From 6888c1e85f5db129e6ddcff879bb127bbfdb5c64 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 19 Jul 2024 18:51:04 +0100 Subject: [PATCH 6/6] gfs2: Remove gfs2_aspace_writepage() There are no remaining callers of gfs2_aspace_writepage() other than vmscan, which is known to do more harm than good. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Andreas Gruenbacher --- fs/gfs2/meta_io.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index cfb204c9396c..fea3efcc2f93 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -86,13 +86,6 @@ static void gfs2_aspace_write_folio(struct folio *folio, folio_end_writeback(folio); } -static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wbc) -{ - gfs2_aspace_write_folio(page_folio(page), wbc); - - return 0; -} - static int gfs2_aspace_writepages(struct address_space *mapping, struct writeback_control *wbc) { @@ -115,7 +108,6 @@ const struct address_space_operations gfs2_meta_aops = { const struct address_space_operations gfs2_rgrp_aops = { .dirty_folio = block_dirty_folio, .invalidate_folio = block_invalidate_folio, - .writepage = gfs2_aspace_writepage, .writepages = gfs2_aspace_writepages, .release_folio = gfs2_release_folio, };