From 5f02d16868b9d738d70656d074518cac760d39ab Mon Sep 17 00:00:00 2001 From: Minjie Du Date: Mon, 17 Jul 2023 15:19:37 +0800 Subject: [PATCH 01/51] gfs2: increase usage of folio_next_index() helper Simplify code pattern of 'folio->index + folio_nr_pages(folio)' by using the existing helper folio_next_index(). Signed-off-by: Minjie Du Signed-off-by: Andreas Gruenbacher --- fs/gfs2/aops.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index ae49256b7c8c..5f02542370c4 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -272,8 +272,7 @@ continue_unlock: * not be suitable for data integrity * writeout). */ - *done_index = folio->index + - folio_nr_pages(folio); + *done_index = folio_next_index(folio); ret = 1; break; } From 111c7d27a1b7954954afde46f9db01d5ad24b316 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 26 Jul 2023 23:17:53 +0200 Subject: [PATCH 02/51] gfs2: Use mapping->gfp_mask for metadata inodes Set mapping->gfp mask to GFP_NOFS for all metadata inodes so that allocating pages in the address space of those inodes won't call back into the filesystem. This allows to switch back from find_or_create_page() to grab_cache_page() in two places. Partially reverts commit 220cca2a4f58 ("GFS2: Change truncate page allocation to be GFP_NOFS"). Thanks to Dan Carpenter for pointing out a Smatch static checker warning. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/inode.c | 14 ++++++++++---- fs/gfs2/lops.c | 7 +++---- fs/gfs2/quota.c | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 17c994a0c0d0..fadf5c157603 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -276,10 +276,16 @@ struct inode *gfs2_lookup_simple(struct inode *dip, const char *name) * gfs2_lookup_simple callers expect ENOENT * and do not check for NULL. */ - if (inode == NULL) - return ERR_PTR(-ENOENT); - else - return inode; + if (IS_ERR_OR_NULL(inode)) + return inode ? inode : ERR_PTR(-ENOENT); + + /* + * Must not call back into the filesystem when allocating + * pages in the metadata inode's address space. + */ + mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); + + return inode; } diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index 251322b01631..483f69807062 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -456,7 +456,7 @@ static bool gfs2_jhead_pg_srch(struct gfs2_jdesc *jd, * Find the folio with 'index' in the journal's mapping. Search the folio for * the journal head if requested (cleanup == false). Release refs on the * folio so the page cache can reclaim it. We grabbed a - * reference on this folio twice, first when we did a find_or_create_page() + * reference on this folio twice, first when we did a grab_cache_page() * to obtain the folio to add it to the bio and second when we do a * filemap_get_folio() here to get the folio to wait on while I/O on it is being * completed. @@ -481,7 +481,7 @@ static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index, if (!*done) *done = gfs2_jhead_pg_srch(jd, head, &folio->page); - /* filemap_get_folio() and the earlier find_or_create_page() */ + /* filemap_get_folio() and the earlier grab_cache_page() */ folio_put_refs(folio, 2); } @@ -535,8 +535,7 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head, for (; block < je->lblock + je->blocks; block++, dblock++) { if (!page) { - page = find_or_create_page(mapping, - block >> shift, GFP_NOFS); + page = grab_cache_page(mapping, block >> shift); if (!page) { ret = -ENOMEM; done = true; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 704192b73605..96d41ee034d7 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -724,7 +724,7 @@ static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, blk = index << (PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift); boff = off % bsize; - page = find_or_create_page(mapping, index, GFP_NOFS); + page = grab_cache_page(mapping, index); if (!page) return -ENOMEM; if (!page_has_buffers(page)) From dc0b9435238c1a68150c798c9c7a1b5d7414cbb9 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 26 Jul 2023 23:29:11 +0200 Subject: [PATCH 03/51] gfs: Don't use GFP_NOFS in gfs2_unstuff_dinode Revert the rest of commit 220cca2a4f58 ("GFS2: Change truncate page allocation to be GFP_NOFS"): In gfs2_unstuff_dinode(), there is no need to carry out the page cache allocation under GFP_NOFS because inodes on the "regular" filesystem are never un-inlined under memory pressure, so switch back from find_or_create_page() to grab_cache_page() here as well. Inodes on the "metadata" filesystem can theoretically be un-inlined under memory pressure, but any page cache allocations in that context would happen in GFP_NOFS context because those inodes have inode->i_mapping->gfp_mask set to GFP_NOFS (see the previous patch). Signed-off-by: Andreas Gruenbacher --- fs/gfs2/bmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 8d611fbcf0bd..c2f0ed76a2b6 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -161,7 +161,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip) int error; down_write(&ip->i_rw_mutex); - page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS); + page = grab_cache_page(inode->i_mapping, 0); error = -ENOMEM; if (!page) goto out; From de3e7f97aebb7304856c46daf358ef14f0204219 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 8 Aug 2023 22:46:34 +0200 Subject: [PATCH 04/51] gfs2: do_promote cleanup Change function do_promote to return true on success, and false otherwise. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 1438e7465e30..d0c82b721f90 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -468,10 +468,10 @@ done: * do_promote - promote as many requests as possible on the current queue * @gl: The glock * - * Returns: 1 if there is a blocked holder at the head of the list + * Returns true on success (i.e., progress was made or there are no waiters). */ -static int do_promote(struct gfs2_glock *gl) +static bool do_promote(struct gfs2_glock *gl) { struct gfs2_holder *gh, *current_gh; @@ -484,10 +484,10 @@ static int do_promote(struct gfs2_glock *gl) * If we get here, it means we may not grant this * holder for some reason. If this holder is at the * head of the list, it means we have a blocked holder - * at the head, so return 1. + * at the head, so return false. */ if (list_is_first(&gh->gh_list, &gl->gl_holders)) - return 1; + return false; do_error(gl, 0); break; } @@ -497,7 +497,7 @@ static int do_promote(struct gfs2_glock *gl) if (!current_gh) current_gh = gh; } - return 0; + return true; } /** @@ -834,7 +834,7 @@ __acquires(&gl->gl_lockref.lock) } else { if (test_bit(GLF_DEMOTE, &gl->gl_flags)) gfs2_demote_wake(gl); - if (do_promote(gl) == 0) + if (do_promote(gl)) goto out_unlock; gh = find_first_waiter(gl); gl->gl_target = gh->gh_state; From 0b93bac2271e11beb980fca037a34a9819c7dc37 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 8 Aug 2023 20:27:46 +0200 Subject: [PATCH 05/51] gfs2: Remove LM_FLAG_PRIORITY flag The last user of this flag was removed in commit b77b4a4815a9 ("gfs2: Rework freeze / thaw logic"). Signed-off-by: Andreas Gruenbacher --- Documentation/filesystems/gfs2-glocks.rst | 3 +-- fs/gfs2/glock.c | 23 ++++++----------------- fs/gfs2/glock.h | 9 --------- fs/gfs2/lock_dlm.c | 5 ----- 4 files changed, 7 insertions(+), 33 deletions(-) diff --git a/Documentation/filesystems/gfs2-glocks.rst b/Documentation/filesystems/gfs2-glocks.rst index d14f230f0b12..93a690b9bcf2 100644 --- a/Documentation/filesystems/gfs2-glocks.rst +++ b/Documentation/filesystems/gfs2-glocks.rst @@ -20,8 +20,7 @@ The gl_holders list contains all the queued lock requests (not just the holders) associated with the glock. If there are any held locks, then they will be contiguous entries at the head of the list. Locks are granted in strictly the order that they -are queued, except for those marked LM_FLAG_PRIORITY which are -used only during recovery, and even then only for journal locks. +are queued. There are three lock states that users of the glock layer can request, namely shared (SH), deferred (DF) and exclusive (EX). Those translate diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index d0c82b721f90..72346f3306e4 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -591,8 +591,7 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret) if (gh && !test_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags)) { /* move to back of queue and try next entry */ if (ret & LM_OUT_CANCELED) { - if ((gh->gh_flags & LM_FLAG_PRIORITY) == 0) - list_move_tail(&gh->gh_list, &gl->gl_holders); + list_move_tail(&gh->gh_list, &gl->gl_holders); gh = find_first_waiter(gl); gl->gl_target = gh->gh_state; goto retry; @@ -679,8 +678,7 @@ __acquires(&gl->gl_lockref.lock) gh && !(gh->gh_flags & LM_FLAG_NOEXP)) goto skip_inval; - lck_flags &= (LM_FLAG_TRY | LM_FLAG_TRY_1CB | LM_FLAG_NOEXP | - LM_FLAG_PRIORITY); + lck_flags &= (LM_FLAG_TRY | LM_FLAG_TRY_1CB | LM_FLAG_NOEXP); GLOCK_BUG_ON(gl, gl->gl_state == target); GLOCK_BUG_ON(gl, gl->gl_state == gl->gl_target); if ((target == LM_ST_UNLOCKED || target == LM_ST_DEFERRED) && @@ -1515,27 +1513,20 @@ fail: } if (test_bit(HIF_HOLDER, &gh2->gh_iflags)) continue; - if (unlikely((gh->gh_flags & LM_FLAG_PRIORITY) && !insert_pt)) - insert_pt = &gh2->gh_list; } trace_gfs2_glock_queue(gh, 1); gfs2_glstats_inc(gl, GFS2_LKS_QCOUNT); gfs2_sbstats_inc(gl, GFS2_LKS_QCOUNT); if (likely(insert_pt == NULL)) { list_add_tail(&gh->gh_list, &gl->gl_holders); - if (unlikely(gh->gh_flags & LM_FLAG_PRIORITY)) - goto do_cancel; return; } list_add_tail(&gh->gh_list, insert_pt); -do_cancel: gh = list_first_entry(&gl->gl_holders, struct gfs2_holder, gh_list); - if (!(gh->gh_flags & LM_FLAG_PRIORITY)) { - spin_unlock(&gl->gl_lockref.lock); - if (sdp->sd_lockstruct.ls_ops->lm_cancel) - sdp->sd_lockstruct.ls_ops->lm_cancel(gl); - spin_lock(&gl->gl_lockref.lock); - } + spin_unlock(&gl->gl_lockref.lock); + if (sdp->sd_lockstruct.ls_ops->lm_cancel) + sdp->sd_lockstruct.ls_ops->lm_cancel(gl); + spin_lock(&gl->gl_lockref.lock); return; trap_recursive: @@ -2227,8 +2218,6 @@ static const char *hflags2str(char *buf, u16 flags, unsigned long iflags) *p++ = 'e'; if (flags & LM_FLAG_ANY) *p++ = 'A'; - if (flags & LM_FLAG_PRIORITY) - *p++ = 'p'; if (flags & LM_FLAG_NODE_SCOPE) *p++ = 'n'; if (flags & GL_ASYNC) diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 1f1ba92c15a8..c8685ca7d2a2 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -68,14 +68,6 @@ enum { * also be granted in SHARED. The preferred state is whichever is compatible * with other granted locks, or the specified state if no other locks exist. * - * LM_FLAG_PRIORITY - * Override fairness considerations. Suppose a lock is held in a shared state - * and there is a pending request for the deferred state. A shared lock - * request with the priority flag would be allowed to bypass the deferred - * request and directly join the other shared lock. A shared lock request - * without the priority flag might be forced to wait until the deferred - * requested had acquired and released the lock. - * * LM_FLAG_NODE_SCOPE * This holder agrees to share the lock within this node. In other words, * the glock is held in EX mode according to DLM, but local holders on the @@ -86,7 +78,6 @@ enum { #define LM_FLAG_TRY_1CB 0x0002 #define LM_FLAG_NOEXP 0x0004 #define LM_FLAG_ANY 0x0008 -#define LM_FLAG_PRIORITY 0x0010 #define LM_FLAG_NODE_SCOPE 0x0020 #define GL_ASYNC 0x0040 #define GL_EXACT 0x0080 diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 54911294687c..59ab18c79889 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -222,11 +222,6 @@ static u32 make_flags(struct gfs2_glock *gl, const unsigned int gfs_flags, lkf |= DLM_LKF_NOQUEUEBAST; } - if (gfs_flags & LM_FLAG_PRIORITY) { - lkf |= DLM_LKF_NOORDER; - lkf |= DLM_LKF_HEADQUE; - } - if (gfs_flags & LM_FLAG_ANY) { if (req == DLM_LOCK_PR) lkf |= DLM_LKF_ALTCW; From 66fa9912ec973796de4ce9b430d0811d401c17bb Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 26 Jul 2023 12:01:08 -0500 Subject: [PATCH 06/51] gfs2: conversion deadlock do_promote bypass Consider the following case: 1. A glock is held in shared mode. 2. A process requests the glock in exclusive mode (rename). 3. Before the lock is granted, more processes (read / ls) request the glock in shared mode again. 4. gfs2 sends a request to dlm for the lock in exclusive mode because that holder is at the head of the queue. 5. Somehow the dlm request gets canceled, so dlm sends us back a response with state == LM_ST_SHARED and LM_OUT_CANCELED. So at that point, the glock is still held in shared mode. 6. finish_xmote gets called to process the response from dlm. It detects that the glock is not in the requested mode and no demote is in progress, so it moves the canceled holder to the tail of the queue and finds the new holder at the head of the queue. That holder is requesting the glock in shared mode. 7. finish_xmote calls do_xmote to transition the glock into shared mode, but the glock is already in shared mode and so do_xmote complains about that with: GLOCK_BUG_ON(gl, gl->gl_state == gl->gl_target); Instead, in finish_xmote, after moving the canceled holder to the tail of the queue, check if any new holders can be granted. Only call do_xmote to repeat the dlm request if the holder at the head of the queue is requesting the glock in a mode that is incompatible with the mode the glock is currently held in. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 72346f3306e4..675bfec77706 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -594,6 +594,8 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret) list_move_tail(&gh->gh_list, &gl->gl_holders); gh = find_first_waiter(gl); gl->gl_target = gh->gh_state; + if (do_promote(gl)) + goto out; goto retry; } /* Some error or failed "try lock" - report it */ From 6df373b09b1dcf2f7d579f515f653f89a896d417 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Aug 2023 15:46:16 +0200 Subject: [PATCH 07/51] gfs2: Switch to wait_event in gfs2_logd In gfs2_logd(), switch from an open-coded wait loop to wait_event_interruptible_timeout(). Signed-off-by: Andreas Gruenbacher --- fs/gfs2/log.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index aa568796207c..d3da259820e3 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -1301,7 +1301,6 @@ int gfs2_logd(void *data) { struct gfs2_sbd *sdp = data; unsigned long t = 1; - DEFINE_WAIT(wait); while (!kthread_should_stop()) { @@ -1338,17 +1337,11 @@ int gfs2_logd(void *data) try_to_freeze(); - do { - prepare_to_wait(&sdp->sd_logd_waitq, &wait, - TASK_INTERRUPTIBLE); - if (!gfs2_ail_flush_reqd(sdp) && - !gfs2_jrnl_flush_reqd(sdp) && - !kthread_should_stop()) - t = schedule_timeout(t); - } while(t && !gfs2_ail_flush_reqd(sdp) && - !gfs2_jrnl_flush_reqd(sdp) && - !kthread_should_stop()); - finish_wait(&sdp->sd_logd_waitq, &wait); + t = wait_event_interruptible_timeout(sdp->sd_logd_waitq, + gfs2_ail_flush_reqd(sdp) || + gfs2_jrnl_flush_reqd(sdp) || + kthread_should_stop(), + t); } return 0; From b74cd55aa9a9d0aca760028a51343ec79812e410 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 10 Aug 2023 17:15:46 +0200 Subject: [PATCH 08/51] gfs2: low-memory forced flush fixes First, function gfs2_ail_flush_reqd checks the SDF_FORCE_AIL_FLUSH flag to determine if an AIL flush should be forced in low-memory situations. However, it also immediately clears the flag, and when called repeatedly as in function gfs2_logd, the flag will be lost. Fix that by pulling the SDF_FORCE_AIL_FLUSH flag check out of gfs2_ail_flush_reqd. Second, function gfs2_writepages sets the SDF_FORCE_AIL_FLUSH flag whether or not enough pages were written. If enough pages could be written, flushing the AIL is unnecessary, though. Third, gfs2_writepages doesn't wake up logd after setting the SDF_FORCE_AIL_FLUSH flag, so it can take a long time for logd to react. It would be preferable to wake up logd, but that hurts the performance of some workloads and we don't quite understand why so far, so don't wake up logd so far. Fixes: b066a4eebd4f ("gfs2: forcibly flush ail to relieve memory pressure") Signed-off-by: Andreas Gruenbacher --- fs/gfs2/aops.c | 4 ++-- fs/gfs2/log.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 5f02542370c4..add62aa2df21 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -183,13 +183,13 @@ static int gfs2_writepages(struct address_space *mapping, int ret; /* - * Even if we didn't write any pages here, we might still be holding + * Even if we didn't write enough pages here, we might still be holding * dirty pages in the ail. We forcibly flush the ail because we don't * want balance_dirty_pages() to loop indefinitely trying to write out * pages held in the ail that it can't find. */ ret = iomap_writepages(mapping, wbc, &wpc, &gfs2_writeback_ops); - if (ret == 0) + if (ret == 0 && wbc->nr_to_write > 0) set_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags); return ret; } diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index d3da259820e3..aaca22f2aa2d 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -1282,9 +1282,6 @@ static inline int gfs2_ail_flush_reqd(struct gfs2_sbd *sdp) { unsigned int used_blocks = sdp->sd_jdesc->jd_blocks - atomic_read(&sdp->sd_log_blks_free); - if (test_and_clear_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags)) - return 1; - return used_blocks + atomic_read(&sdp->sd_log_blks_needed) >= atomic_read(&sdp->sd_log_thresh2); } @@ -1325,7 +1322,9 @@ int gfs2_logd(void *data) GFS2_LFC_LOGD_JFLUSH_REQD); } - if (gfs2_ail_flush_reqd(sdp)) { + if (test_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags) || + gfs2_ail_flush_reqd(sdp)) { + clear_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags); gfs2_ail1_start(sdp); gfs2_ail1_wait(sdp); gfs2_ail1_empty(sdp, 0); @@ -1338,6 +1337,7 @@ int gfs2_logd(void *data) try_to_freeze(); t = wait_event_interruptible_timeout(sdp->sd_logd_waitq, + test_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags) || gfs2_ail_flush_reqd(sdp) || gfs2_jrnl_flush_reqd(sdp) || kthread_should_stop(), From b6b8f72a11b9d0d7badc7b51030f7fecf695fd79 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Aug 2023 18:36:36 +0200 Subject: [PATCH 09/51] gfs2: Fix logd wakeup on I/O error When quotad detects an I/O error, it sets sd_log_error and then it wakes up logd to withdraw the filesystem. However, logd doesn't wake up when sd_log_error is set. Fix that. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index aaca22f2aa2d..abe4397dc59b 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -1340,6 +1340,7 @@ int gfs2_logd(void *data) test_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags) || gfs2_ail_flush_reqd(sdp) || gfs2_jrnl_flush_reqd(sdp) || + sdp->sd_log_error || kthread_should_stop(), t); } From db77789bae7e33b458220110f189f2381f19362b Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Fri, 11 Aug 2023 16:00:10 +0200 Subject: [PATCH 10/51] gfs2: journal flush threshold fixes and cleanup Commit f07b35202148 ("GFS2: Made logd daemon take into account log demand") changed gfs2_ail_flush_reqd() and gfs2_jrnl_flush_reqd() to take sd_log_blks_needed into account, but the checks in gfs2_log_commit() were not updated correspondingly. Once that is fixed, gfs2_jrnl_flush_reqd() and gfs2_ail_flush_reqd() can be used in gfs2_log_commit(). Make those two helpers available to gfs2_log_commit() by defining them above gfs2_log_commit(). Signed-off-by: Andreas Gruenbacher --- fs/gfs2/log.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index abe4397dc59b..addf4ce0bedd 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -1227,6 +1227,21 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) gfs2_log_unlock(sdp); } +static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp) +{ + return atomic_read(&sdp->sd_log_pinned) + + atomic_read(&sdp->sd_log_blks_needed) >= + atomic_read(&sdp->sd_log_thresh1); +} + +static inline int gfs2_ail_flush_reqd(struct gfs2_sbd *sdp) +{ + return sdp->sd_jdesc->jd_blocks - + atomic_read(&sdp->sd_log_blks_free) + + atomic_read(&sdp->sd_log_blks_needed) >= + atomic_read(&sdp->sd_log_thresh2); +} + /** * gfs2_log_commit - Commit a transaction to the log * @sdp: the filesystem @@ -1246,9 +1261,7 @@ void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { log_refund(sdp, tr); - if (atomic_read(&sdp->sd_log_pinned) > atomic_read(&sdp->sd_log_thresh1) || - ((sdp->sd_jdesc->jd_blocks - atomic_read(&sdp->sd_log_blks_free)) > - atomic_read(&sdp->sd_log_thresh2))) + if (gfs2_ail_flush_reqd(sdp) || gfs2_jrnl_flush_reqd(sdp)) wake_up(&sdp->sd_logd_waitq); } @@ -1271,21 +1284,6 @@ static void gfs2_log_shutdown(struct gfs2_sbd *sdp) gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list)); } -static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp) -{ - return (atomic_read(&sdp->sd_log_pinned) + - atomic_read(&sdp->sd_log_blks_needed) >= - atomic_read(&sdp->sd_log_thresh1)); -} - -static inline int gfs2_ail_flush_reqd(struct gfs2_sbd *sdp) -{ - unsigned int used_blocks = sdp->sd_jdesc->jd_blocks - atomic_read(&sdp->sd_log_blks_free); - - return used_blocks + atomic_read(&sdp->sd_log_blks_needed) >= - atomic_read(&sdp->sd_log_thresh2); -} - /** * gfs2_logd - Update log tail as Active Items get flushed to in-place blocks * @data: Pointer to GFS2 superblock From 481f6e7d734ad9a00b44cf0c000f0ed30843e4d3 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 16 Jun 2023 10:46:50 -0500 Subject: [PATCH 11/51] gfs2: Use qd_sbd more consequently Before this patch many of the functions in quota.c got their superblock pointer, sdp, from the quota_data's glock pointer. That's silly because the qd already has its own pointer to the superblock (qd_sbd). This patch changes references to use that instead, eliminating a level of indirection. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 96d41ee034d7..48b9fbffe260 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -119,7 +119,7 @@ static void gfs2_qd_dispose(struct list_head *list) while (!list_empty(list)) { qd = list_first_entry(list, struct gfs2_quota_data, qd_lru); - sdp = qd->qd_gl->gl_name.ln_sbd; + sdp = qd->qd_sbd; list_del(&qd->qd_lru); @@ -302,7 +302,7 @@ static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, static void qd_hold(struct gfs2_quota_data *qd) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; gfs2_assert(sdp, !__lockref_is_dead(&qd->qd_lockref)); lockref_get(&qd->qd_lockref); } @@ -367,7 +367,7 @@ static void slot_put(struct gfs2_quota_data *qd) static int bh_get(struct gfs2_quota_data *qd) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; struct inode *inode = sdp->sd_qc_inode; struct gfs2_inode *ip = GFS2_I(inode); unsigned int block, offset; @@ -421,7 +421,7 @@ fail: static void bh_put(struct gfs2_quota_data *qd) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; mutex_lock(&sdp->sd_quota_mutex); gfs2_assert(sdp, qd->qd_bh_count); @@ -489,8 +489,7 @@ static int qd_fish(struct gfs2_sbd *sdp, struct gfs2_quota_data **qdp) static void qd_unlock(struct gfs2_quota_data *qd) { - gfs2_assert_warn(qd->qd_gl->gl_name.ln_sbd, - test_bit(QDF_LOCKED, &qd->qd_flags)); + gfs2_assert_warn(qd->qd_sbd, test_bit(QDF_LOCKED, &qd->qd_flags)); clear_bit(QDF_LOCKED, &qd->qd_flags); bh_put(qd); slot_put(qd); @@ -666,7 +665,7 @@ static int sort_qd(const void *a, const void *b) static void do_qc(struct gfs2_quota_data *qd, s64 change, int qc_type) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode); struct gfs2_quota_change *qc = qd->qd_bh_qc; s64 x; @@ -881,7 +880,7 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) { - struct gfs2_sbd *sdp = (*qda)->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = (*qda)->qd_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); struct gfs2_alloc_parms ap = { .aflags = 0, }; unsigned int data_blocks, ind_blocks; @@ -1009,11 +1008,12 @@ static int update_qd(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd) static int do_glock(struct gfs2_quota_data *qd, int force_refresh, struct gfs2_holder *q_gh) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); struct gfs2_holder i_gh; int error; + gfs2_assert_warn(sdp, sdp == qd->qd_gl->gl_name.ln_sbd); restart: error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_SHARED, 0, q_gh); if (error) @@ -1091,7 +1091,7 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) static int need_sync(struct gfs2_quota_data *qd) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; struct gfs2_tune *gt = &sdp->sd_tune; s64 value; unsigned int num, den; @@ -1178,7 +1178,7 @@ void gfs2_quota_unlock(struct gfs2_inode *ip) static int print_message(struct gfs2_quota_data *qd, char *type) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; fs_info(sdp, "quota %s for %s %u\n", type, From 3c69c437bf9832d2201702c5ccc3b8a77a7e0aa3 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 23 Aug 2023 15:49:58 +0200 Subject: [PATCH 12/51] gfs2: Rename sd_{ glock => kill }_wait Rename sd_glock_wait to sd_kill_wait: we'll use it for other things related to "killing" a filesystem on unmount soon (kill_sb). Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 6 +++--- fs/gfs2/incore.h | 2 +- fs/gfs2/ops_fstype.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 675bfec77706..e242745cf40f 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -176,7 +176,7 @@ void gfs2_glock_free(struct gfs2_glock *gl) wake_up_glock(gl); call_rcu(&gl->gl_rcu, gfs2_glock_dealloc); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) - wake_up(&sdp->sd_glock_wait); + wake_up(&sdp->sd_kill_wait); } /** @@ -1231,7 +1231,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, out_free: gfs2_glock_dealloc(&gl->gl_rcu); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) - wake_up(&sdp->sd_glock_wait); + wake_up(&sdp->sd_kill_wait); out: return ret; @@ -2188,7 +2188,7 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp) flush_workqueue(glock_workqueue); glock_hash_walk(clear_glock, sdp); flush_workqueue(glock_workqueue); - wait_event_timeout(sdp->sd_glock_wait, + wait_event_timeout(sdp->sd_kill_wait, atomic_read(&sdp->sd_glock_disposal) == 0, HZ * 600); glock_hash_walk(dump_glock_func, sdp); diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 04f2d78e8658..7abb43bb8df0 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -716,7 +716,7 @@ struct gfs2_sbd { struct gfs2_glock *sd_rename_gl; struct gfs2_glock *sd_freeze_gl; struct work_struct sd_freeze_work; - wait_queue_head_t sd_glock_wait; + wait_queue_head_t sd_kill_wait; wait_queue_head_t sd_async_glock_wait; atomic_t sd_glock_disposal; struct completion sd_locking_init; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 8a27957dbfee..19d19491c0d1 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -87,7 +87,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) set_bit(SDF_NOJOURNALID, &sdp->sd_flags); gfs2_tune_init(&sdp->sd_tune); - init_waitqueue_head(&sdp->sd_glock_wait); + init_waitqueue_head(&sdp->sd_kill_wait); init_waitqueue_head(&sdp->sd_async_glock_wait); atomic_set(&sdp->sd_glock_disposal, 0); init_completion(&sdp->sd_locking_init); From e7beb8b6de1a6d6956fe0652d85cf356416ce4d9 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 23 Aug 2023 15:53:13 +0200 Subject: [PATCH 13/51] gfs2: Rename SDF_DEACTIVATING to SDF_KILL Rename the SDF_DEACTIVATING flag to SDF_KILL to make it more obvious that this relates to the kill_sb filesystem operation. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 4 ++-- fs/gfs2/glops.c | 2 +- fs/gfs2/incore.h | 2 +- fs/gfs2/ops_fstype.c | 4 ++-- fs/gfs2/super.c | 2 +- fs/gfs2/sys.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index e242745cf40f..9cbf8d98489a 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1022,7 +1022,7 @@ static void delete_work_func(struct work_struct *work) * step entirely. */ if (gfs2_try_evict(gl)) { - if (test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) + if (test_bit(SDF_KILL, &sdp->sd_flags)) goto out; if (gfs2_queue_verify_evict(gl)) return; @@ -1035,7 +1035,7 @@ static void delete_work_func(struct work_struct *work) GFS2_BLKST_UNLINKED); if (IS_ERR(inode)) { if (PTR_ERR(inode) == -EAGAIN && - !test_bit(SDF_DEACTIVATING, &sdp->sd_flags) && + !test_bit(SDF_KILL, &sdp->sd_flags) && gfs2_queue_verify_evict(gl)) return; } else { diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 54319328b16b..7d6cca467fa1 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -637,7 +637,7 @@ static void iopen_go_callback(struct gfs2_glock *gl, bool remote) struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; if (!remote || sb_rdonly(sdp->sd_vfs) || - test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) + test_bit(SDF_KILL, &sdp->sd_flags)) return; if (gl->gl_demote_state == LM_ST_UNLOCKED && diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 7abb43bb8df0..ab857431dfa4 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -606,7 +606,7 @@ enum { SDF_REMOTE_WITHDRAW = 13, /* Performing remote recovery */ SDF_WITHDRAW_RECOVERY = 14, /* Wait for journal recovery when we are withdrawing */ - SDF_DEACTIVATING = 15, + SDF_KILL = 15, SDF_EVICTING = 16, SDF_FROZEN = 17, }; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 19d19491c0d1..6ea295cee463 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1786,9 +1786,9 @@ static void gfs2_kill_sb(struct super_block *sb) /* * Flush and then drain the delete workqueue here (via * destroy_workqueue()) to ensure that any delete work that - * may be running will also see the SDF_DEACTIVATING flag. + * may be running will also see the SDF_KILL flag. */ - set_bit(SDF_DEACTIVATING, &sdp->sd_flags); + set_bit(SDF_KILL, &sdp->sd_flags); gfs2_flush_delete_work(sdp); destroy_workqueue(sdp->sd_delete_wq); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 9f4d5d6549ee..e0dceef3c9cc 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -546,7 +546,7 @@ void gfs2_make_fs_ro(struct gfs2_sbd *sdp) { int log_write_allowed = test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); - if (!test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) + if (!test_bit(SDF_KILL, &sdp->sd_flags)) gfs2_flush_delete_work(sdp); if (!log_write_allowed && current == sdp->sd_quotad_process) diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 2dfbe2f188dd..a85fe7b92911 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -118,7 +118,7 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) test_bit(SDF_WITHDRAW_IN_PROG, &f), test_bit(SDF_REMOTE_WITHDRAW, &f), test_bit(SDF_WITHDRAW_RECOVERY, &f), - test_bit(SDF_DEACTIVATING, &f), + test_bit(SDF_KILL, &f), sdp->sd_log_error, rwsem_is_locked(&sdp->sd_log_flush_lock), sdp->sd_log_num_revoke, From 6b0e9a5f1e6d7f2719856b601d5639902fc0ee4f Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 22 Aug 2023 22:59:27 +0200 Subject: [PATCH 14/51] gfs2: Fix wrong quota shrinker return value Function gfs2_qd_isolate must only return LRU_REMOVED when removing the item from the lru list; otherwise, the number of items on the list will go wrong. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 48b9fbffe260..f58072efafc9 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -149,18 +149,22 @@ static enum lru_status gfs2_qd_isolate(struct list_head *item, struct list_lru_one *lru, spinlock_t *lru_lock, void *arg) { struct list_head *dispose = arg; - struct gfs2_quota_data *qd = list_entry(item, struct gfs2_quota_data, qd_lru); + struct gfs2_quota_data *qd = + list_entry(item, struct gfs2_quota_data, qd_lru); + enum lru_status status; if (!spin_trylock(&qd->qd_lockref.lock)) return LRU_SKIP; + status = LRU_SKIP; if (qd->qd_lockref.count == 0) { lockref_mark_dead(&qd->qd_lockref); list_lru_isolate_move(lru, &qd->qd_lru, dispose); + status = LRU_REMOVED; } spin_unlock(&qd->qd_lockref.lock); - return LRU_REMOVED; + return status; } static unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, From 961fe3422e055d251b32a117cd8cd3d27153bc96 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 23 Aug 2023 21:38:04 +0200 Subject: [PATCH 15/51] gfs2: Use gfs2_qd_dispose in gfs2_quota_cleanup Change gfs2_quota_cleanup() to move the quota data objects to dispose of on a dispose list and call gfs2_qd_dispose() on that list, like gfs2_qd_shrink_scan() does, instead of disposing of the quota data objects directly. This may look a bit pointless by itself, but it will make more sense in combination with a fix that follows. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index f58072efafc9..976bf1097706 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1459,35 +1459,17 @@ fail: void gfs2_quota_cleanup(struct gfs2_sbd *sdp) { - struct list_head *head = &sdp->sd_quota_list; struct gfs2_quota_data *qd; + LIST_HEAD(dispose); spin_lock(&qd_lock); - while (!list_empty(head)) { - qd = list_last_entry(head, struct gfs2_quota_data, qd_list); - - list_del(&qd->qd_list); - - /* Also remove if this qd exists in the reclaim list */ + list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { list_lru_del(&gfs2_qd_lru, &qd->qd_lru); - atomic_dec(&sdp->sd_quota_count); - spin_unlock(&qd_lock); - - spin_lock_bucket(qd->qd_hash); - hlist_bl_del_rcu(&qd->qd_hlist); - spin_unlock_bucket(qd->qd_hash); - - gfs2_assert_warn(sdp, !qd->qd_change); - gfs2_assert_warn(sdp, !qd->qd_slot_count); - gfs2_assert_warn(sdp, !qd->qd_bh_count); - - gfs2_glock_put(qd->qd_gl); - call_rcu(&qd->qd_rcu, gfs2_qd_dealloc); - - spin_lock(&qd_lock); + list_add(&qd->qd_lru, &dispose); } spin_unlock(&qd_lock); + gfs2_qd_dispose(&dispose); gfs2_assert_warn(sdp, !atomic_read(&sdp->sd_quota_count)); kvfree(sdp->sd_quota_bitmap); From faada74a90563183cb57af3e4604ed183d71a81c Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 22 Aug 2023 14:18:21 +0200 Subject: [PATCH 16/51] gfs2: Factor out duplicate quota data disposal code Rename gfs2_qd_dispose() to gfs2_qd_dispose_list(). Move some code duplicated in gfs2_qd_dispose_list() and gfs2_quota_cleanup() into a new gfs2_qd_dispose() function. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 976bf1097706..01fae6b030e9 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -112,35 +112,36 @@ static void gfs2_qd_dealloc(struct rcu_head *rcu) kmem_cache_free(gfs2_quotad_cachep, qd); } -static void gfs2_qd_dispose(struct list_head *list) +static void gfs2_qd_dispose(struct gfs2_quota_data *qd) +{ + struct gfs2_sbd *sdp = qd->qd_sbd; + + spin_lock(&qd_lock); + list_del(&qd->qd_list); + spin_unlock(&qd_lock); + + spin_lock_bucket(qd->qd_hash); + hlist_bl_del_rcu(&qd->qd_hlist); + spin_unlock_bucket(qd->qd_hash); + + gfs2_assert_warn(sdp, !qd->qd_change); + gfs2_assert_warn(sdp, !qd->qd_slot_count); + gfs2_assert_warn(sdp, !qd->qd_bh_count); + + gfs2_glock_put(qd->qd_gl); + call_rcu(&qd->qd_rcu, gfs2_qd_dealloc); +} + +static void gfs2_qd_list_dispose(struct list_head *list) { struct gfs2_quota_data *qd; - struct gfs2_sbd *sdp; while (!list_empty(list)) { qd = list_first_entry(list, struct gfs2_quota_data, qd_lru); - sdp = qd->qd_sbd; - list_del(&qd->qd_lru); - /* Free from the filesystem-specific list */ - spin_lock(&qd_lock); - list_del(&qd->qd_list); - spin_unlock(&qd_lock); - - spin_lock_bucket(qd->qd_hash); - hlist_bl_del_rcu(&qd->qd_hlist); - spin_unlock_bucket(qd->qd_hash); - - gfs2_assert_warn(sdp, !qd->qd_change); - gfs2_assert_warn(sdp, !qd->qd_slot_count); - gfs2_assert_warn(sdp, !qd->qd_bh_count); - - gfs2_glock_put(qd->qd_gl); + gfs2_qd_dispose(qd); atomic_dec(&sdp->sd_quota_count); - - /* Delete it from the common reclaim list */ - call_rcu(&qd->qd_rcu, gfs2_qd_dealloc); } } @@ -179,7 +180,7 @@ static unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, freed = list_lru_shrink_walk(&gfs2_qd_lru, sc, gfs2_qd_isolate, &dispose); - gfs2_qd_dispose(&dispose); + gfs2_qd_list_dispose(&dispose); return freed; } @@ -1469,7 +1470,7 @@ void gfs2_quota_cleanup(struct gfs2_sbd *sdp) } spin_unlock(&qd_lock); - gfs2_qd_dispose(&dispose); + gfs2_qd_list_dispose(&dispose); gfs2_assert_warn(sdp, !atomic_read(&sdp->sd_quota_count)); kvfree(sdp->sd_quota_bitmap); From fae2e73a5537e40ebae9a00e6548101e5ab2441f Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Aug 2023 15:08:55 +0200 Subject: [PATCH 17/51] gfs2: No more quota complaints after withdraw Once a filesystem is withdrawn, don't complain about quota changes that can't be synced to the main quota file anymore. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 01fae6b030e9..fccdb22980e8 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -124,9 +124,11 @@ static void gfs2_qd_dispose(struct gfs2_quota_data *qd) hlist_bl_del_rcu(&qd->qd_hlist); spin_unlock_bucket(qd->qd_hash); - gfs2_assert_warn(sdp, !qd->qd_change); - gfs2_assert_warn(sdp, !qd->qd_slot_count); - gfs2_assert_warn(sdp, !qd->qd_bh_count); + if (!gfs2_withdrawn(sdp)) { + gfs2_assert_warn(sdp, !qd->qd_change); + gfs2_assert_warn(sdp, !qd->qd_slot_count); + gfs2_assert_warn(sdp, !qd->qd_bh_count); + } gfs2_glock_put(qd->qd_gl); call_rcu(&qd->qd_rcu, gfs2_qd_dealloc); From bb73ae8ff394f767a8acbc99a1d682b874ba5e74 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 24 Aug 2023 14:52:29 +0200 Subject: [PATCH 18/51] gfs2: Fix initial quota data refcount Fix the refcount of quota data objects created directly by gfs2_quota_init(): those are placed into the in-memory quota "database" for eventual syncing to the main quota file, but they are not actively held and should thus have an initial refcount of 0. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index fccdb22980e8..97fdf64148ba 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -228,7 +228,7 @@ static struct gfs2_quota_data *qd_alloc(unsigned hash, struct gfs2_sbd *sdp, str return NULL; qd->qd_sbd = sdp; - qd->qd_lockref.count = 1; + qd->qd_lockref.count = 0; spin_lock_init(&qd->qd_lockref.lock); qd->qd_id = qid; qd->qd_slot = -1; @@ -290,6 +290,7 @@ static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, spin_lock_bucket(hash); *qdp = qd = gfs2_qd_search_bucket(hash, sdp, qid); if (qd == NULL) { + new_qd->qd_lockref.count++; *qdp = new_qd; list_add(&new_qd->qd_list, &sdp->sd_quota_list); hlist_bl_add_head_rcu(&new_qd->qd_hlist, &qd_hash_table[hash]); From a475c5dd16e57c570113eccba51955b5df8bb052 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 23 Aug 2023 17:15:47 +0200 Subject: [PATCH 19/51] gfs2: Free quota data objects synchronously In gfs2_quota_cleanup(), wait for the quota data objects to be freed before returning. Otherwise, there is no guarantee that the quota data objects will be gone when their kmem cache is destroyed. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 97fdf64148ba..75be0d8e243f 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -109,7 +109,11 @@ static inline void spin_unlock_bucket(unsigned int hash) static void gfs2_qd_dealloc(struct rcu_head *rcu) { struct gfs2_quota_data *qd = container_of(rcu, struct gfs2_quota_data, qd_rcu); + struct gfs2_sbd *sdp = qd->qd_sbd; + kmem_cache_free(gfs2_quotad_cachep, qd); + if (atomic_dec_and_test(&sdp->sd_quota_count)) + wake_up(&sdp->sd_kill_wait); } static void gfs2_qd_dispose(struct gfs2_quota_data *qd) @@ -143,7 +147,6 @@ static void gfs2_qd_list_dispose(struct list_head *list) list_del(&qd->qd_lru); gfs2_qd_dispose(qd); - atomic_dec(&sdp->sd_quota_count); } } @@ -317,13 +320,24 @@ static void qd_hold(struct gfs2_quota_data *qd) static void qd_put(struct gfs2_quota_data *qd) { + struct gfs2_sbd *sdp; + if (lockref_put_or_lock(&qd->qd_lockref)) return; + BUG_ON(__lockref_is_dead(&qd->qd_lockref)); + sdp = qd->qd_sbd; + if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) { + lockref_mark_dead(&qd->qd_lockref); + spin_unlock(&qd->qd_lockref.lock); + + gfs2_qd_dispose(qd); + return; + } + qd->qd_lockref.count = 0; list_lru_add(&gfs2_qd_lru, &qd->qd_lru); spin_unlock(&qd->qd_lockref.lock); - } static int slot_get(struct gfs2_quota_data *qd) @@ -1465,16 +1479,33 @@ void gfs2_quota_cleanup(struct gfs2_sbd *sdp) { struct gfs2_quota_data *qd; LIST_HEAD(dispose); + int count; + + BUG_ON(test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)); spin_lock(&qd_lock); list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { + spin_lock(&qd->qd_lockref.lock); + if (qd->qd_lockref.count != 0) { + spin_unlock(&qd->qd_lockref.lock); + continue; + } + lockref_mark_dead(&qd->qd_lockref); + spin_unlock(&qd->qd_lockref.lock); + list_lru_del(&gfs2_qd_lru, &qd->qd_lru); list_add(&qd->qd_lru, &dispose); } spin_unlock(&qd_lock); gfs2_qd_list_dispose(&dispose); - gfs2_assert_warn(sdp, !atomic_read(&sdp->sd_quota_count)); + + wait_event_timeout(sdp->sd_kill_wait, + (count = atomic_read(&sdp->sd_quota_count)) == 0, + HZ * 60); + + if (count != 0) + fs_err(sdp, "%d left-over quota data objects\n", count); kvfree(sdp->sd_quota_bitmap); sdp->sd_quota_bitmap = NULL; From f66af88e33212b57ea86da2c5d66c0d9d5c46344 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 28 Aug 2023 16:39:20 +0200 Subject: [PATCH 20/51] gfs2: Stop using gfs2_make_fs_ro for withdraw [ 81.372851][ T5532] CPU: 1 PID: 5532 Comm: syz-executor.0 Not tainted 6.2.0-rc1-syzkaller-dirty #0 [ 81.382080][ T5532] Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/12/2023 [ 81.392343][ T5532] Call Trace: [ 81.395654][ T5532] [ 81.398603][ T5532] dump_stack_lvl+0x1b1/0x290 [ 81.418421][ T5532] gfs2_assert_warn_i+0x19a/0x2e0 [ 81.423480][ T5532] gfs2_quota_cleanup+0x4c6/0x6b0 [ 81.428611][ T5532] gfs2_make_fs_ro+0x517/0x610 [ 81.457802][ T5532] gfs2_withdraw+0x609/0x1540 [ 81.481452][ T5532] gfs2_inode_refresh+0xb2d/0xf60 [ 81.506658][ T5532] gfs2_instantiate+0x15e/0x220 [ 81.511504][ T5532] gfs2_glock_wait+0x1d9/0x2a0 [ 81.516352][ T5532] do_sync+0x485/0xc80 [ 81.554943][ T5532] gfs2_quota_sync+0x3da/0x8b0 [ 81.559738][ T5532] gfs2_sync_fs+0x49/0xb0 [ 81.564063][ T5532] sync_filesystem+0xe8/0x220 [ 81.568740][ T5532] generic_shutdown_super+0x6b/0x310 [ 81.574112][ T5532] kill_block_super+0x79/0xd0 [ 81.578779][ T5532] deactivate_locked_super+0xa7/0xf0 [ 81.584064][ T5532] cleanup_mnt+0x494/0x520 [ 81.593753][ T5532] task_work_run+0x243/0x300 [ 81.608837][ T5532] exit_to_user_mode_loop+0x124/0x150 [ 81.614232][ T5532] exit_to_user_mode_prepare+0xb2/0x140 [ 81.619820][ T5532] syscall_exit_to_user_mode+0x26/0x60 [ 81.625287][ T5532] do_syscall_64+0x49/0xb0 [ 81.629710][ T5532] entry_SYSCALL_64_after_hwframe+0x63/0xcd In this backtrace, gfs2_quota_sync() takes quota data references and then calls do_sync(). Function do_sync() encounters filesystem corruption and withdraws the filesystem, which (among other things) calls gfs2_quota_cleanup(). Function gfs2_quota_cleanup() wrongly assumes that nobody is holding any quota data references anymore, and destroys all quota data objects. When gfs2_quota_sync() then resumes and dereferences the quota data objects it is holding, those objects are no longer there. Function gfs2_quota_cleanup() deals with resource deallocation and can easily be delayed until gfs2_put_super() in the case of a filesystem withdraw. In fact, most of the other work gfs2_make_fs_ro() does is unnecessary during a withdraw as well, so change signal_our_withdraw() to skip gfs2_make_fs_ro() and perform the necessary steps directly instead. Thanks to Edward Adam Davis for the initial patches. Link: https://lore.kernel.org/all/0000000000002b5e2405f14e860f@google.com Reported-by: syzbot+3f6a670108ce43356017@syzkaller.appspotmail.com Signed-off-by: Andreas Gruenbacher --- fs/gfs2/super.c | 9 ++------- fs/gfs2/util.c | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index e0dceef3c9cc..119a5b20eb00 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -580,15 +580,8 @@ void gfs2_make_fs_ro(struct gfs2_sbd *sdp) gfs2_log_is_empty(sdp), HZ * 5); gfs2_assert_warn(sdp, gfs2_log_is_empty(sdp)); - } else { - wait_event_timeout(sdp->sd_log_waitq, - gfs2_log_is_empty(sdp), - HZ * 5); } gfs2_quota_cleanup(sdp); - - if (!log_write_allowed) - sdp->sd_vfs->s_flags |= SB_RDONLY; } /** @@ -622,6 +615,8 @@ restart: if (!sb_rdonly(sb)) { gfs2_make_fs_ro(sdp); } + if (gfs2_withdrawn(sdp)) + gfs2_quota_cleanup(sdp); WARN_ON(gfs2_withdrawing(sdp)); /* At this point, we're through modifying the disk */ diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index dac22b1c1a2e..b3086a9baf00 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -150,7 +151,23 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp) if (!sb_rdonly(sdp->sd_vfs)) { bool locked = mutex_trylock(&sdp->sd_freeze_mutex); - gfs2_make_fs_ro(sdp); + if (sdp->sd_quotad_process && + current != sdp->sd_quotad_process) { + kthread_stop(sdp->sd_quotad_process); + sdp->sd_quotad_process = NULL; + } + + if (sdp->sd_logd_process && + current != sdp->sd_logd_process) { + kthread_stop(sdp->sd_logd_process); + sdp->sd_logd_process = NULL; + } + + wait_event_timeout(sdp->sd_log_waitq, + gfs2_log_is_empty(sdp), + HZ * 5); + + sdp->sd_vfs->s_flags |= SB_RDONLY; if (locked) mutex_unlock(&sdp->sd_freeze_mutex); From fe4f7940d212440334f06783e06a15d674013bb1 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 28 Aug 2023 17:14:32 +0200 Subject: [PATCH 21/51] gfs2: Fix asynchronous thread destruction The kernel threads are currently stopped and destroyed synchronously by gfs2_make_fs_ro() and gfs2_put_super(), and asynchronously by signal_our_withdraw(), with no synchronization, so the synchronous and asynchronous contexts can race with each other. First, when creating the kernel threads, take an extra task struct reference so that the task struct won't go away immediately when they terminate. This allows those kthreads to terminate immediately when they're done rather than hanging around as zombies until they are reaped by kthread_stop(). When kthread_stop() is called on a terminated kthread, it will return immediately. Second, in signal_our_withdraw(), once the SDF_JOURNAL_LIVE flag has been cleared, wake up the logd and quotad wait queues instead of stopping the logd and quotad kthreads. The kthreads are then expected to terminate automatically within short time, but if they cannot, they will not block the withdraw. For example, if a user process and one of the kthread decide to withdraw at the same time, only one of them will perform the actual withdraw and the other will wait for it to be done. If the kthread ends up being the one to wait, the withdrawing user process won't be able to stop it. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/ops_fstype.c | 35 +++++++++++++++++++++++++---------- fs/gfs2/super.c | 16 ++++------------ fs/gfs2/super.h | 1 + fs/gfs2/util.c | 13 ++----------- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 6ea295cee463..a1a8be56ead5 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1103,29 +1103,49 @@ static int init_threads(struct gfs2_sbd *sdp) struct task_struct *p; int error = 0; - p = kthread_run(gfs2_logd, sdp, "gfs2_logd"); + p = kthread_create(gfs2_logd, sdp, "gfs2_logd"); if (IS_ERR(p)) { error = PTR_ERR(p); - fs_err(sdp, "can't start logd thread: %d\n", error); + fs_err(sdp, "can't create logd thread: %d\n", error); return error; } + get_task_struct(p); sdp->sd_logd_process = p; - p = kthread_run(gfs2_quotad, sdp, "gfs2_quotad"); + p = kthread_create(gfs2_quotad, sdp, "gfs2_quotad"); if (IS_ERR(p)) { error = PTR_ERR(p); - fs_err(sdp, "can't start quotad thread: %d\n", error); + fs_err(sdp, "can't create quotad thread: %d\n", error); goto fail; } + get_task_struct(p); sdp->sd_quotad_process = p; + + wake_up_process(sdp->sd_logd_process); + wake_up_process(sdp->sd_quotad_process); return 0; fail: kthread_stop(sdp->sd_logd_process); + put_task_struct(sdp->sd_logd_process); sdp->sd_logd_process = NULL; return error; } +void gfs2_destroy_threads(struct gfs2_sbd *sdp) +{ + if (sdp->sd_logd_process) { + kthread_stop(sdp->sd_logd_process); + put_task_struct(sdp->sd_logd_process); + sdp->sd_logd_process = NULL; + } + if (sdp->sd_quotad_process) { + kthread_stop(sdp->sd_quotad_process); + put_task_struct(sdp->sd_quotad_process); + sdp->sd_quotad_process = NULL; + } +} + /** * gfs2_fill_super - Read in superblock * @sb: The VFS superblock @@ -1276,12 +1296,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) if (error) { gfs2_freeze_unlock(&sdp->sd_freeze_gh); - if (sdp->sd_quotad_process) - kthread_stop(sdp->sd_quotad_process); - sdp->sd_quotad_process = NULL; - if (sdp->sd_logd_process) - kthread_stop(sdp->sd_logd_process); - sdp->sd_logd_process = NULL; + gfs2_destroy_threads(sdp); fs_err(sdp, "can't make FS RW: %d\n", error); goto fail_per_node; } diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 119a5b20eb00..be3f69c15edc 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -549,17 +549,7 @@ void gfs2_make_fs_ro(struct gfs2_sbd *sdp) if (!test_bit(SDF_KILL, &sdp->sd_flags)) gfs2_flush_delete_work(sdp); - if (!log_write_allowed && current == sdp->sd_quotad_process) - fs_warn(sdp, "The quotad daemon is withdrawing.\n"); - else if (sdp->sd_quotad_process) - kthread_stop(sdp->sd_quotad_process); - sdp->sd_quotad_process = NULL; - - if (!log_write_allowed && current == sdp->sd_logd_process) - fs_warn(sdp, "The logd daemon is withdrawing.\n"); - else if (sdp->sd_logd_process) - kthread_stop(sdp->sd_logd_process); - sdp->sd_logd_process = NULL; + gfs2_destroy_threads(sdp); if (log_write_allowed) { gfs2_quota_sync(sdp->sd_vfs, 0); @@ -615,8 +605,10 @@ restart: if (!sb_rdonly(sb)) { gfs2_make_fs_ro(sdp); } - if (gfs2_withdrawn(sdp)) + if (gfs2_withdrawn(sdp)) { + gfs2_destroy_threads(sdp); gfs2_quota_cleanup(sdp); + } WARN_ON(gfs2_withdrawing(sdp)); /* At this point, we're through modifying the disk */ diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h index bba58629bc45..ab9c83106932 100644 --- a/fs/gfs2/super.h +++ b/fs/gfs2/super.h @@ -36,6 +36,7 @@ extern int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename, extern int gfs2_make_fs_rw(struct gfs2_sbd *sdp); extern void gfs2_make_fs_ro(struct gfs2_sbd *sdp); extern void gfs2_online_uevent(struct gfs2_sbd *sdp); +extern void gfs2_destroy_threads(struct gfs2_sbd *sdp); extern int gfs2_statfs_init(struct gfs2_sbd *sdp); extern void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free, s64 dinodes); diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index b3086a9baf00..65a3c7b1a51e 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -151,17 +151,8 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp) if (!sb_rdonly(sdp->sd_vfs)) { bool locked = mutex_trylock(&sdp->sd_freeze_mutex); - if (sdp->sd_quotad_process && - current != sdp->sd_quotad_process) { - kthread_stop(sdp->sd_quotad_process); - sdp->sd_quotad_process = NULL; - } - - if (sdp->sd_logd_process && - current != sdp->sd_logd_process) { - kthread_stop(sdp->sd_logd_process); - sdp->sd_logd_process = NULL; - } + wake_up(&sdp->sd_logd_waitq); + wake_up(&sdp->sd_quota_wait); wait_event_timeout(sdp->sd_log_waitq, gfs2_log_is_empty(sdp), From e4a8b5481c59a3f1252f595330c2d2cd038886b2 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Fri, 25 Aug 2023 11:06:20 +0200 Subject: [PATCH 22/51] gfs2: Switch to wait_event in gfs2_quotad In gfs2_quotad(), switch from an open-coded wait loop to wait_event_interruptible_timeout(). Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 75be0d8e243f..5201bb403fc6 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1557,7 +1557,6 @@ int gfs2_quotad(void *data) unsigned long statfs_timeo = 0; unsigned long quotad_timeo = 0; unsigned long t = 0; - DEFINE_WAIT(wait); while (!kthread_should_stop()) { @@ -1583,12 +1582,12 @@ int gfs2_quotad(void *data) bypass: t = min(quotad_timeo, statfs_timeo); - prepare_to_wait(&sdp->sd_quota_wait, &wait, TASK_INTERRUPTIBLE); - if (!sdp->sd_statfs_force_sync) - t -= schedule_timeout(t); - else + t = wait_event_interruptible_timeout(sdp->sd_quota_wait, + sdp->sd_statfs_force_sync, + t); + + if (sdp->sd_statfs_force_sync) t = 0; - finish_wait(&sdp->sd_quota_wait, &wait); } return 0; From fe0690f0a6f190a9ec0736c01ddeba7a729cf30d Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Fri, 25 Aug 2023 11:18:38 +0200 Subject: [PATCH 23/51] gfs2: Sanitize kthread stopping Immediately stop the logd and quotad kernel threads when a filesystem withdraw is detected: those threads aren't doing anything useful after a withdraw. (Depends on the extra logd and quotad task struct references held since commit 7a109f383fa3 ("gfs2: Fix asynchronous thread destruction").) In addition, check for kthread_should_stop() in the wait condition in gfs2_quotad() to stop immediately when kthread_stop() is called. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/log.c | 9 ++++----- fs/gfs2/quota.c | 9 +++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index addf4ce0bedd..e5271ae87d1c 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -1298,11 +1298,9 @@ int gfs2_logd(void *data) unsigned long t = 1; while (!kthread_should_stop()) { + if (gfs2_withdrawn(sdp)) + break; - if (gfs2_withdrawn(sdp)) { - msleep_interruptible(HZ); - continue; - } /* Check for errors writing to the journal */ if (sdp->sd_log_error) { gfs2_lm(sdp, @@ -1311,7 +1309,7 @@ int gfs2_logd(void *data) "prevent further damage.\n", sdp->sd_fsname, sdp->sd_log_error); gfs2_withdraw(sdp); - continue; + break; } if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { @@ -1339,6 +1337,7 @@ int gfs2_logd(void *data) gfs2_ail_flush_reqd(sdp) || gfs2_jrnl_flush_reqd(sdp) || sdp->sd_log_error || + gfs2_withdrawn(sdp) || kthread_should_stop(), t); } diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 5201bb403fc6..3a3189f5104a 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1559,9 +1559,9 @@ int gfs2_quotad(void *data) unsigned long t = 0; while (!kthread_should_stop()) { - if (gfs2_withdrawn(sdp)) - goto bypass; + break; + /* Update the master statfs file */ if (sdp->sd_statfs_force_sync) { int error = gfs2_statfs_sync(sdp->sd_vfs, 0); @@ -1579,11 +1579,12 @@ int gfs2_quotad(void *data) try_to_freeze(); -bypass: t = min(quotad_timeo, statfs_timeo); t = wait_event_interruptible_timeout(sdp->sd_quota_wait, - sdp->sd_statfs_force_sync, + sdp->sd_statfs_force_sync || + gfs2_withdrawn(sdp) || + kthread_should_stop(), t); if (sdp->sd_statfs_force_sync) From e3da6be3d70449a1f14c99f13cc1eb453a2be4ea Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 30 Aug 2023 22:09:36 +0200 Subject: [PATCH 24/51] gfs2: Fix withdraw race Function gfs2_withdraw() tries to synchronize concurrent callers by atomically setting the SDF_WITHDRAWN flag in the first caller, setting the SDF_WITHDRAW_IN_PROG flag to indicate that a withdraw is in progress, performing the actual withdraw, and clearing the SDF_WITHDRAW_IN_PROG flag when done. All other callers wait for the SDF_WITHDRAW_IN_PROG flag to be cleared before returning. This leaves a small window in which callers can find the SDF_WITHDRAWN flag set before the SDF_WITHDRAW_IN_PROG flag has been set, causing them to return prematurely, before the withdraw has been completed. Fix that by setting the SDF_WITHDRAWN and SDF_WITHDRAW_IN_PROG flags atomically. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/util.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index 65a3c7b1a51e..da29fafb6272 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -323,19 +323,19 @@ int gfs2_withdraw(struct gfs2_sbd *sdp) struct lm_lockstruct *ls = &sdp->sd_lockstruct; const struct lm_lockops *lm = ls->ls_ops; - if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW && - test_and_set_bit(SDF_WITHDRAWN, &sdp->sd_flags)) { - if (!test_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags)) - return -1; - - wait_on_bit(&sdp->sd_flags, SDF_WITHDRAW_IN_PROG, - TASK_UNINTERRUPTIBLE); - return -1; - } - - set_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags); - if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) { + unsigned long old = READ_ONCE(sdp->sd_flags), new; + + do { + if (old & BIT(SDF_WITHDRAWN)) { + wait_on_bit(&sdp->sd_flags, + SDF_WITHDRAW_IN_PROG, + TASK_UNINTERRUPTIBLE); + return -1; + } + new = old | BIT(SDF_WITHDRAWN) | BIT(SDF_WITHDRAW_IN_PROG); + } while (unlikely(!try_cmpxchg(&sdp->sd_flags, &old, new))); + fs_err(sdp, "about to withdraw this file system\n"); BUG_ON(sdp->sd_args.ar_debug); From 5c0dc371a28e9a1eb4b80093f37c79d28305cc18 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 28 Aug 2023 18:02:22 +0200 Subject: [PATCH 25/51] gfs2: Rename "gfs_recovery" workqueue to "gfs2_recovery" Rename the "gfs_recovery" workqueue to "gfs2_recovery", and gfs_recovery_wq to gfs2_recovery_wq. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/main.c | 8 ++++---- fs/gfs2/recovery.c | 4 ++-- fs/gfs2/recovery.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index afcb32854f14..69bded82f376 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -152,9 +152,9 @@ static int __init init_gfs2_fs(void) goto fail_shrinker; error = -ENOMEM; - gfs_recovery_wq = alloc_workqueue("gfs_recovery", + gfs2_recovery_wq = alloc_workqueue("gfs2_recovery", WQ_MEM_RECLAIM | WQ_FREEZABLE, 0); - if (!gfs_recovery_wq) + if (!gfs2_recovery_wq) goto fail_wq1; gfs2_control_wq = alloc_workqueue("gfs2_control", @@ -194,7 +194,7 @@ fail_mempool: fail_wq3: destroy_workqueue(gfs2_control_wq); fail_wq2: - destroy_workqueue(gfs_recovery_wq); + destroy_workqueue(gfs2_recovery_wq); fail_wq1: unregister_shrinker(&gfs2_qd_shrinker); fail_shrinker: @@ -234,7 +234,7 @@ static void __exit exit_gfs2_fs(void) gfs2_unregister_debugfs(); unregister_filesystem(&gfs2_fs_type); unregister_filesystem(&gfs2meta_fs_type); - destroy_workqueue(gfs_recovery_wq); + destroy_workqueue(gfs2_recovery_wq); destroy_workqueue(gfs2_control_wq); destroy_workqueue(gfs2_freeze_wq); list_lru_destroy(&gfs2_qd_lru); diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 9c7a9f640bad..5aae02669a40 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -27,7 +27,7 @@ #include "util.h" #include "dir.h" -struct workqueue_struct *gfs_recovery_wq; +struct workqueue_struct *gfs2_recovery_wq; int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk, struct buffer_head **bh) @@ -570,7 +570,7 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd, bool wait) return -EBUSY; /* we have JDF_RECOVERY, queue should always succeed */ - rv = queue_work(gfs_recovery_wq, &jd->jd_work); + rv = queue_work(gfs2_recovery_wq, &jd->jd_work); BUG_ON(!rv); if (wait) diff --git a/fs/gfs2/recovery.h b/fs/gfs2/recovery.h index 0d30f8e804f4..7a0c9d0b7503 100644 --- a/fs/gfs2/recovery.h +++ b/fs/gfs2/recovery.h @@ -9,7 +9,7 @@ #include "incore.h" -extern struct workqueue_struct *gfs_recovery_wq; +extern struct workqueue_struct *gfs2_recovery_wq; static inline void gfs2_replay_incr_blk(struct gfs2_jdesc *jd, u32 *blk) { From ab8eecf5d0a7b8e8b06e282aeb051dc37eecaf21 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 28 Aug 2023 18:03:30 +0200 Subject: [PATCH 26/51] gfs2: Rename "freeze_workqueue" to "gfs2_freeze" Signed-off-by: Andreas Gruenbacher --- fs/gfs2/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index 69bded82f376..66eb98b690a2 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -162,7 +162,7 @@ static int __init init_gfs2_fs(void) if (!gfs2_control_wq) goto fail_wq2; - gfs2_freeze_wq = alloc_workqueue("freeze_workqueue", 0, 0); + gfs2_freeze_wq = alloc_workqueue("gfs2_freeze", 0, 0); if (!gfs2_freeze_wq) goto fail_wq3; From 267d1a011ec2345a320e84c16d8b91411f165aa8 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 28 Aug 2023 18:05:44 +0200 Subject: [PATCH 27/51] gfs2: Add device name to gfs2_logd and gfs2_quotad Add the device name to the names of the gfs2_logd and gfs2_quotad kernel threads to allow for easier identification. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/ops_fstype.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index a1a8be56ead5..bcbe95229e89 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1103,7 +1103,7 @@ static int init_threads(struct gfs2_sbd *sdp) struct task_struct *p; int error = 0; - p = kthread_create(gfs2_logd, sdp, "gfs2_logd"); + p = kthread_create(gfs2_logd, sdp, "gfs2_logd/%s", sdp->sd_fsname); if (IS_ERR(p)) { error = PTR_ERR(p); fs_err(sdp, "can't create logd thread: %d\n", error); @@ -1112,7 +1112,7 @@ static int init_threads(struct gfs2_sbd *sdp) get_task_struct(p); sdp->sd_logd_process = p; - p = kthread_create(gfs2_quotad, sdp, "gfs2_quotad"); + p = kthread_create(gfs2_quotad, sdp, "gfs2_quotad/%s", sdp->sd_fsname); if (IS_ERR(p)) { error = PTR_ERR(p); fs_err(sdp, "can't create quotad thread: %d\n", error); From eef46ab713f78591fe2cb20f5e90d9a8fdbddd59 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 28 Jun 2023 13:52:42 -0500 Subject: [PATCH 28/51] gfs2: Introduce new quota=quiet mount option This patch adds a new mount option quota=quiet which is the same as quota=on but it suppresses gfs2 quota error messages. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/incore.h | 1 + fs/gfs2/ops_fstype.c | 1 + fs/gfs2/quota.c | 17 +++++++++++------ fs/gfs2/super.c | 3 +++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index ab857431dfa4..7e94cf3562d6 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -537,6 +537,7 @@ struct gfs2_statfs_change_host { #define GFS2_QUOTA_OFF 0 #define GFS2_QUOTA_ACCOUNT 1 #define GFS2_QUOTA_ON 2 +#define GFS2_QUOTA_QUIET 3 /* on but not complaining */ #define GFS2_DATA_DEFAULT GFS2_DATA_ORDERED #define GFS2_DATA_WRITEBACK 1 diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index bcbe95229e89..33ca04733e93 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1396,6 +1396,7 @@ static const struct constant_table gfs2_param_quota[] = { {"off", GFS2_QUOTA_OFF}, {"account", GFS2_QUOTA_ACCOUNT}, {"on", GFS2_QUOTA_ON}, + {"quiet", GFS2_QUOTA_QUIET}, {} }; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 3a3189f5104a..0cc585064eae 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1083,7 +1083,8 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) u32 x; int error = 0; - if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) + if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON && + sdp->sd_args.ar_quota != GFS2_QUOTA_QUIET) return 0; error = gfs2_quota_hold(ip, uid, gid); @@ -1202,10 +1203,11 @@ static int print_message(struct gfs2_quota_data *qd, char *type) { struct gfs2_sbd *sdp = qd->qd_sbd; - fs_info(sdp, "quota %s for %s %u\n", - type, - (qd->qd_id.type == USRQUOTA) ? "user" : "group", - from_kqid(&init_user_ns, qd->qd_id)); + if (sdp->sd_args.ar_quota != GFS2_QUOTA_QUIET) + fs_info(sdp, "quota %s for %s %u\n", + type, + (qd->qd_id.type == USRQUOTA) ? "user" : "group", + from_kqid(&init_user_ns, qd->qd_id)); return 0; } @@ -1291,7 +1293,8 @@ void gfs2_quota_change(struct gfs2_inode *ip, s64 change, u32 x; struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON || + if ((sdp->sd_args.ar_quota != GFS2_QUOTA_ON && + sdp->sd_args.ar_quota != GFS2_QUOTA_QUIET) || gfs2_assert_warn(sdp, change)) return; if (ip->i_diskflags & GFS2_DIF_SYSTEM) @@ -1601,6 +1604,8 @@ static int gfs2_quota_get_state(struct super_block *sb, struct qc_state *state) memset(state, 0, sizeof(*state)); switch (sdp->sd_args.ar_quota) { + case GFS2_QUOTA_QUIET: + fallthrough; case GFS2_QUOTA_ON: state->s_state[USRQUOTA].flags |= QCI_LIMITS_ENFORCED; state->s_state[GRPQUOTA].flags |= QCI_LIMITS_ENFORCED; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index be3f69c15edc..18993ecc2f70 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1119,6 +1119,9 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root) case GFS2_QUOTA_ON: state = "on"; break; + case GFS2_QUOTA_QUIET: + state = "quiet"; + break; default: state = "unknown"; break; From 768963ab07e5219d7ae84a6e4ec61bc59bc4b81d Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 31 May 2023 10:08:49 -0500 Subject: [PATCH 29/51] gfs2: remove dead code for quota writes Since patch 845802b112ee function gfs2_write_buf_to_page checks if the target inode is jdata or ordered. This function only operates on the system quota file, which is always jdata, so the check for jdata is useless. This patch removes it. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 0cc585064eae..2f311118df9b 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -772,10 +772,7 @@ static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, set_buffer_uptodate(bh); if (bh_read(bh, REQ_META | REQ_PRIO) < 0) goto unlock_out; - if (gfs2_is_jdata(ip)) - gfs2_trans_add_data(ip->i_gl, bh); - else - gfs2_ordered_add_inode(ip); + gfs2_trans_add_data(ip->i_gl, bh); /* If we need to write to the next block as well */ if (to_write > (bsize - boff)) { From ee1768e467a9c0f272192da1ce0ea5a9caf98ccc Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 31 May 2023 10:25:29 -0500 Subject: [PATCH 30/51] gfs2: Pass sdp to gfs2_adjust_quota Before this change function gfs2_adjust_quota's first parameter was an gfs2_inode pointer. But it always pointed to the quota inode. Here we switch that to pass the superblock pointer, sdp, so it is easier to read the code and understand that it's only dealing with the quota inode. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 2f311118df9b..3d78026c63c4 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -830,7 +830,7 @@ static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, /** * gfs2_adjust_quota - adjust record of current block usage - * @ip: The quota inode + * @sdp: The superblock * @loc: Offset of the entry in the quota file * @change: The amount of usage change to record * @qd: The quota data @@ -842,12 +842,12 @@ static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, * Returns: 0 or -ve on error */ -static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, +static int gfs2_adjust_quota(struct gfs2_sbd *sdp, loff_t loc, s64 change, struct gfs2_quota_data *qd, struct qc_dqblk *fdq) { + struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); struct inode *inode = &ip->i_inode; - struct gfs2_sbd *sdp = GFS2_SB(inode); struct gfs2_quota q; int err; u64 size; @@ -971,7 +971,8 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) for (x = 0; x < num_qd; x++) { qd = qda[x]; offset = qd2offset(qd); - error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync, qd, NULL); + error = gfs2_adjust_quota(sdp, offset, qd->qd_change_sync, qd, + NULL); if (error) goto out_end_trans; @@ -1749,7 +1750,7 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, goto out_release; /* Apply changes */ - error = gfs2_adjust_quota(ip, offset, 0, qd, fdq); + error = gfs2_adjust_quota(sdp, offset, 0, qd, fdq); if (!error) clear_bit(QDF_QMSG_QUIET, &qd->qd_flags); From adfd2b5e4f87590fcd0abd756c71b0aefe20dbf4 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 31 May 2023 10:31:33 -0500 Subject: [PATCH 31/51] gfs2: pass sdp in to gfs2_write_disk_quota Like the previous patch, we now pass the superblock pointer to function gfs2_write_disk_quota. This makes the code more understandable, since it only operates on the quota inode. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 3d78026c63c4..3dade83bd358 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -798,9 +798,10 @@ unlock_out: return -EIO; } -static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, +static int gfs2_write_disk_quota(struct gfs2_sbd *sdp, struct gfs2_quota *qp, loff_t loc) { + struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); unsigned long pg_beg; unsigned pg_off, nbytes, overflow = 0; int pg_oflow = 0, error; @@ -884,7 +885,7 @@ static int gfs2_adjust_quota(struct gfs2_sbd *sdp, loff_t loc, } } - err = gfs2_write_disk_quota(ip, &q, loc); + err = gfs2_write_disk_quota(sdp, &q, loc); if (!err) { size = loc + sizeof(struct gfs2_quota); if (size > inode->i_size) From d96dad2715672c8b9f16b54216da46514aaeb453 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 31 May 2023 10:39:42 -0500 Subject: [PATCH 32/51] gfs2: pass sdp to gfs2_write_buf_to_page This patch passes the superblock pointer to gfs2_write_buf_to_page so it becomes more apparent it's dealing with the system quota file. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 3dade83bd358..7b79fcc65763 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -729,11 +729,11 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change, int qc_type) mutex_unlock(&sdp->sd_quota_mutex); } -static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, +static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index, unsigned off, void *buf, unsigned bytes) { + struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); struct inode *inode = &ip->i_inode; - struct gfs2_sbd *sdp = GFS2_SB(inode); struct address_space *mapping = inode->i_mapping; struct page *page; struct buffer_head *bh; @@ -801,7 +801,6 @@ unlock_out: static int gfs2_write_disk_quota(struct gfs2_sbd *sdp, struct gfs2_quota *qp, loff_t loc) { - struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); unsigned long pg_beg; unsigned pg_off, nbytes, overflow = 0; int pg_oflow = 0, error; @@ -819,11 +818,11 @@ static int gfs2_write_disk_quota(struct gfs2_sbd *sdp, struct gfs2_quota *qp, } ptr = qp; - error = gfs2_write_buf_to_page(ip, pg_beg, pg_off, ptr, + error = gfs2_write_buf_to_page(sdp, pg_beg, pg_off, ptr, nbytes - overflow); /* If there's an overflow, write the remaining bytes to the next page */ if (!error && pg_oflow) - error = gfs2_write_buf_to_page(ip, pg_beg + 1, 0, + error = gfs2_write_buf_to_page(sdp, pg_beg + 1, 0, ptr + nbytes - overflow, overflow); return error; From f0418e4b568ac4759336e1ff8a53582ec88ea966 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 31 May 2023 10:47:17 -0500 Subject: [PATCH 33/51] gfs2: remove unneeded variable done Function gfs2_write_buf_to_page uses variable done to exit its loop, but it's unnecessary if we just code an infinite loop and exit when we need. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 7b79fcc65763..59c6fa01455d 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -740,7 +740,6 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index, u64 blk; unsigned bsize = sdp->sd_sb.sb_bsize, bnum = 0, boff = 0; unsigned to_write = bytes, pg_off = off; - int done = 0; blk = index << (PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift); boff = off % bsize; @@ -752,7 +751,7 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index, create_empty_buffers(page, bsize, 0); bh = page_buffers(page); - while (!done) { + for(;;) { /* Find the beginning block within the page */ if (pg_off >= ((bnum * bsize) + bsize)) { bh = bh->b_this_page; @@ -781,7 +780,7 @@ static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index, boff = pg_off % bsize; continue; } - done = 1; + break; } /* Write to the page, now that we have setup the buffer(s) */ From e34c16c9c699461e7d3f9eed28069e3b8466d871 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 31 May 2023 11:41:16 -0500 Subject: [PATCH 34/51] gfs2: remove unneeded pg_oflow variable Function gfs2_write_disk_quota checks if its write overflows onto another page, and if so, does a second write. Before this patch it kept two variables for this, but only one is needed. This patch simplifies it by eliminating pg_oflow. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 59c6fa01455d..678b21cad303 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -802,7 +802,7 @@ static int gfs2_write_disk_quota(struct gfs2_sbd *sdp, struct gfs2_quota *qp, { unsigned long pg_beg; unsigned pg_off, nbytes, overflow = 0; - int pg_oflow = 0, error; + int error; void *ptr; nbytes = sizeof(struct gfs2_quota); @@ -811,16 +811,14 @@ static int gfs2_write_disk_quota(struct gfs2_sbd *sdp, struct gfs2_quota *qp, pg_off = offset_in_page(loc); /* If the quota straddles a page boundary, split the write in two */ - if ((pg_off + nbytes) > PAGE_SIZE) { - pg_oflow = 1; + if ((pg_off + nbytes) > PAGE_SIZE) overflow = (pg_off + nbytes) - PAGE_SIZE; - } ptr = qp; error = gfs2_write_buf_to_page(sdp, pg_beg, pg_off, ptr, nbytes - overflow); /* If there's an overflow, write the remaining bytes to the next page */ - if (!error && pg_oflow) + if (!error && overflow) error = gfs2_write_buf_to_page(sdp, pg_beg + 1, 0, ptr + nbytes - overflow, overflow); From 2a4f651167560adae05e644e41d2666d727c549b Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 31 May 2023 12:31:52 -0500 Subject: [PATCH 35/51] gfs2: Simplify function need_sync This patch simplifies function need_sync by eliminating a variable in favor of just returning the appropriate value as soon as we know it. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 678b21cad303..fa1d48d5c112 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1107,16 +1107,15 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) return error; } -static int need_sync(struct gfs2_quota_data *qd) +static bool need_sync(struct gfs2_quota_data *qd) { struct gfs2_sbd *sdp = qd->qd_sbd; struct gfs2_tune *gt = &sdp->sd_tune; s64 value; unsigned int num, den; - int do_sync = 1; if (!qd->qd_qb.qb_limit) - return 0; + return false; spin_lock(&qd_lock); value = qd->qd_change; @@ -1128,19 +1127,19 @@ static int need_sync(struct gfs2_quota_data *qd) spin_unlock(>->gt_spin); if (value < 0) - do_sync = 0; + return false; else if ((s64)be64_to_cpu(qd->qd_qb.qb_value) >= (s64)be64_to_cpu(qd->qd_qb.qb_limit)) - do_sync = 0; + return false; else { value *= gfs2_jindex_size(sdp) * num; value = div_s64(value, den); value += (s64)be64_to_cpu(qd->qd_qb.qb_value); if (value < (s64)be64_to_cpu(qd->qd_qb.qb_limit)) - do_sync = 0; + return false; } - return do_sync; + return true; } void gfs2_quota_unlock(struct gfs2_inode *ip) @@ -1156,7 +1155,7 @@ void gfs2_quota_unlock(struct gfs2_inode *ip) for (x = 0; x < ip->i_qadata->qa_qd_num; x++) { struct gfs2_quota_data *qd; - int sync; + bool sync; qd = ip->i_qadata->qa_qd[x]; sync = need_sync(qd); From 9f494e9bdcc54eaed94fcc8fe5d4d5a51c36834c Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 30 Aug 2023 07:57:47 -0500 Subject: [PATCH 36/51] gfs2: Don't try to sync non-changes Function need_sync is supposed to determine if a qd element needs to be synced. If the "change" (qd_change) is zero, it does not need to be synced because there's literally no change in the value. Before this patch need_sync returned false if value < 0. That should be <= 0. This patch changes the check to <=. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index fa1d48d5c112..9be210f3dbd8 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1126,7 +1126,7 @@ static bool need_sync(struct gfs2_quota_data *qd) den = gt->gt_quota_scale_den; spin_unlock(>->gt_spin); - if (value < 0) + if (value <= 0) return false; else if ((s64)be64_to_cpu(qd->qd_qb.qb_value) >= (s64)be64_to_cpu(qd->qd_qb.qb_limit)) From 03d468f1c0469707086f260d4544a2552c7bfa6e Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 16 Jun 2023 06:59:02 -0500 Subject: [PATCH 37/51] gfs2: improvements to sysfs status This patch adds some new fields to the gfs2 status file in sysfs to aid in debugging. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/sys.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index a85fe7b92911..32d5be85382e 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -98,7 +98,10 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) "sd_log_flush_head: %d\n" "sd_log_flush_tail: %d\n" "sd_log_blks_reserved: %d\n" - "sd_log_revokes_available: %d\n", + "sd_log_revokes_available: %d\n" + "sd_log_pinned: %d\n" + "sd_log_thresh1: %d\n" + "sd_log_thresh2: %d\n", test_bit(SDF_JOURNAL_CHECKED, &f), test_bit(SDF_JOURNAL_LIVE, &f), (sdp->sd_jdesc ? sdp->sd_jdesc->jd_jid : 0), @@ -128,7 +131,10 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) sdp->sd_log_flush_head, sdp->sd_log_flush_tail, sdp->sd_log_blks_reserved, - atomic_read(&sdp->sd_log_revokes_available)); + atomic_read(&sdp->sd_log_revokes_available), + atomic_read(&sdp->sd_log_pinned), + atomic_read(&sdp->sd_log_thresh1), + atomic_read(&sdp->sd_log_thresh2)); return s; } From a4d22e337d022d7bb8da25c18629bac2af24ec81 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 16 Jun 2023 12:36:51 -0500 Subject: [PATCH 38/51] gfs2: move qdsb_put and reduce redundancy This patch looks more invasive than it is. It simply moves function qdsb_put before qd_unlock, then changes qd_unlock to call it rather than open coding it. Again, this reduces redundancy. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 9be210f3dbd8..21100feb1db7 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -509,13 +509,18 @@ static int qd_fish(struct gfs2_sbd *sdp, struct gfs2_quota_data **qdp) return 0; } +static void qdsb_put(struct gfs2_quota_data *qd) +{ + bh_put(qd); + slot_put(qd); + qd_put(qd); +} + static void qd_unlock(struct gfs2_quota_data *qd) { gfs2_assert_warn(qd->qd_sbd, test_bit(QDF_LOCKED, &qd->qd_flags)); clear_bit(QDF_LOCKED, &qd->qd_flags); - bh_put(qd); - slot_put(qd); - qd_put(qd); + qdsb_put(qd); } static int qdsb_get(struct gfs2_sbd *sdp, struct kqid qid, @@ -544,13 +549,6 @@ fail: return error; } -static void qdsb_put(struct gfs2_quota_data *qd) -{ - bh_put(qd); - slot_put(qd); - qd_put(qd); -} - /** * gfs2_qa_get - make sure we have a quota allocations data structure, * if necessary From f511e60a55c5f2c8a9781efb67f1897a3b3aebb1 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Tue, 20 Jun 2023 13:49:57 -0500 Subject: [PATCH 39/51] gfs2: Small gfs2_quota_lock cleanup No need to set error = 0 since it's set further down. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 21100feb1db7..c4da8c01deb7 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1074,7 +1074,7 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data *qd; u32 x; - int error = 0; + int error; if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON && sdp->sd_args.ar_quota != GFS2_QUOTA_QUIET) From dec64ae37bf90c2ae3fba2089f5bd1d025c57995 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 21 Jun 2023 12:31:15 -0500 Subject: [PATCH 40/51] gfs2: Remove useless err set Function gfs2_adjust_quota set variable err, then set it again to a different value. This patch removes the redundant set. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index c4da8c01deb7..6824c7497d99 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -859,7 +859,6 @@ static int gfs2_adjust_quota(struct gfs2_sbd *sdp, loff_t loc, return err; loc -= sizeof(q); /* gfs2_internal_read would've advanced the loc ptr */ - err = -EIO; be64_add_cpu(&q.qu_value, change); if (((s64)be64_to_cpu(q.qu_value)) < 0) q.qu_value = 0; /* Never go negative on quota usage */ From fce17cb0eebfb90618f0fcfbdc7f8e0aa207c81a Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 22 Jun 2023 11:04:45 -0500 Subject: [PATCH 41/51] gfs2: Set qd_sync_gen in do_sync Func do_sync was called in two places: gfs2_quota_unlock and gfs2_quota_sync. In gfs2_quota_sync it updated qd_sync_gen to the latest superblock sync gen, if do_sync was successful. In gfs2_quota_unlock it didn't update the value. That can only lead to extra work, for example, if the value is synced by gfs2_quota_unlock but still has the old value. This patch moves the setting of qd_sync_gen inside do_sync so we are guaranteed consistency. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 6824c7497d99..e12d54104485 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -991,6 +991,10 @@ out_dq: GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_DO_SYNC); out: gfs2_qa_put(ip); + if (!error) { + for (x = 0; x < num_qd; x++) + qda[x]->qd_sync_gen = sdp->sd_quota_sync_gen; + } return error; } @@ -1334,10 +1338,6 @@ int gfs2_quota_sync(struct super_block *sb, int type) if (num_qd) { if (!error) error = do_sync(num_qd, qda); - if (!error) - for (x = 0; x < num_qd; x++) - qda[x]->qd_sync_gen = - sdp->sd_quota_sync_gen; for (x = 0; x < num_qd; x++) qd_unlock(qda[x]); From c9ff3c65c26b8f89c6b925ce257fac348559d484 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 22 Jun 2023 11:37:37 -0500 Subject: [PATCH 42/51] gfs2: use constant for array size Function gfs2_quota_unlock declared an array of 4 qd elements. We have a constant for that, we should be using it. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index e12d54104485..9c7234256ebc 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1146,7 +1146,7 @@ static bool need_sync(struct gfs2_quota_data *qd) void gfs2_quota_unlock(struct gfs2_inode *ip) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_quota_data *qda[4]; + struct gfs2_quota_data *qda[2 * GFS2_MAXQUOTAS]; unsigned int count = 0; u32 x; int found; From 3932e5073011567d24ce66cf66575d4d81ea8c94 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 22 Jun 2023 12:36:59 -0500 Subject: [PATCH 43/51] gfs2: Remove quota allocation info from quota file Function do_sync called gfs2_qa_get and put for quota allocation data. But the inode in question is the system master quota file, which is never subject to quotas. Therefore, a qa structure should be unnecessary and if anything accesses it, it's probably a bug. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 9c7234256ebc..ecadbcdad9bf 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -905,18 +905,12 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) unsigned int nalloc = 0, blocks; int error; - error = gfs2_qa_get(ip); - if (error) - return error; - gfs2_write_calc_reserv(ip, sizeof(struct gfs2_quota), &data_blocks, &ind_blocks); ghs = kmalloc_array(num_qd, sizeof(struct gfs2_holder), GFP_NOFS); - if (!ghs) { - error = -ENOMEM; - goto out; - } + if (!ghs) + return -ENOMEM; sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL); inode_lock(&ip->i_inode); @@ -989,8 +983,6 @@ out_dq: kfree(ghs); gfs2_log_flush(ip->i_gl->gl_name.ln_sbd, ip->i_gl, GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_DO_SYNC); -out: - gfs2_qa_put(ip); if (!error) { for (x = 0; x < num_qd; x++) qda[x]->qd_sync_gen = sdp->sd_quota_sync_gen; From 7dbc6ae60dd7089d8ed42892b6a66c138f0aa7a0 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 23 Jun 2023 08:07:55 -0500 Subject: [PATCH 44/51] gfs2: introduce qd_bh_get_or_undo This patch is an attempt to force some consistency in quota sync processing. Two functions (qd_fish and gfs2_quota_unlock) called qd_check_sync, after which they both called bh_get, and if that failed, they took the same steps to undo the actions of qd_check_sync. This patch introduces a new function, qd_bh_get_or_undo, which performs the same steps, reducing code redundancy. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index ecadbcdad9bf..aba8d31a9a7f 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -473,6 +473,20 @@ static int qd_check_sync(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd, return 1; } +static int qd_bh_get_or_undo(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd) +{ + int error; + + error = bh_get(qd); + if (!error) + return 0; + + clear_bit(QDF_LOCKED, &qd->qd_flags); + slot_put(qd); + qd_put(qd); + return error; +} + static int qd_fish(struct gfs2_sbd *sdp, struct gfs2_quota_data **qdp) { struct gfs2_quota_data *qd = NULL, *iter; @@ -495,17 +509,12 @@ static int qd_fish(struct gfs2_sbd *sdp, struct gfs2_quota_data **qdp) spin_unlock(&qd_lock); if (qd) { - error = bh_get(qd); - if (error) { - clear_bit(QDF_LOCKED, &qd->qd_flags); - slot_put(qd); - qd_put(qd); + error = qd_bh_get_or_undo(sdp, qd); + if (error) return error; - } + *qdp = qd; } - *qdp = qd; - return 0; } @@ -1164,15 +1173,8 @@ void gfs2_quota_unlock(struct gfs2_inode *ip) if (!found) continue; - gfs2_assert_warn(sdp, qd->qd_change_sync); - if (bh_get(qd)) { - clear_bit(QDF_LOCKED, &qd->qd_flags); - slot_put(qd); - qd_put(qd); - continue; - } - - qda[count++] = qd; + if (!qd_bh_get_or_undo(sdp, qd)) + qda[count++] = qd; } if (count) { From 8f190c97a4f559bc7e8db739026ebb2d0f53ebed Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Mon, 26 Jun 2023 10:55:52 -0500 Subject: [PATCH 45/51] gfs2: Simplify qd2offset This is a minor cleanup of function qd2offset. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index aba8d31a9a7f..d45a3a1ed9fe 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -213,12 +213,7 @@ static u64 qd2index(struct gfs2_quota_data *qd) static u64 qd2offset(struct gfs2_quota_data *qd) { - u64 offset; - - offset = qd2index(qd); - offset *= sizeof(struct gfs2_quota); - - return offset; + return qd2index(qd) * sizeof(struct gfs2_quota); } static struct gfs2_quota_data *qd_alloc(unsigned hash, struct gfs2_sbd *sdp, struct kqid qid) From 9ab7b78a13aff5bcb3b76380f551ed5cf4125280 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 28 Jun 2023 07:40:30 -0500 Subject: [PATCH 46/51] gfs2: simplify slot_get Simplify function slot_get and get rid of the goto that jumps into the middle of an else branch. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index d45a3a1ed9fe..8b6a1c14a03e 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -342,20 +342,19 @@ static int slot_get(struct gfs2_quota_data *qd) int error = 0; spin_lock(&sdp->sd_bitmap_lock); - if (qd->qd_slot_count != 0) - goto out; - - error = -ENOSPC; - bit = find_first_zero_bit(sdp->sd_quota_bitmap, sdp->sd_quota_slots); - if (bit < sdp->sd_quota_slots) { + if (qd->qd_slot_count == 0) { + bit = find_first_zero_bit(sdp->sd_quota_bitmap, + sdp->sd_quota_slots); + if (bit >= sdp->sd_quota_slots) { + error = -ENOSPC; + goto out; + } set_bit(bit, sdp->sd_quota_bitmap); qd->qd_slot = bit; - error = 0; -out: - qd->qd_slot_count++; } + qd->qd_slot_count++; +out: spin_unlock(&sdp->sd_bitmap_lock); - return error; } From 36a740916a94ea0efa0c5c2ddcd9dcdce56c9a40 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 28 Jun 2023 08:50:46 -0500 Subject: [PATCH 47/51] gfs2: Remove useless assignment This assignment is unnecessary because if error was not already 0, it would have branched to an error label already. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 8b6a1c14a03e..678d2e749db8 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -971,8 +971,6 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) set_bit(QDF_REFRESH, &qd->qd_flags); } - error = 0; - out_end_trans: gfs2_trans_end(sdp); out_ipres: From 06aa6fd31a5f402b055e12ea53bb7b086359d3c8 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 8 Jun 2023 12:30:16 -0500 Subject: [PATCH 48/51] gfs2: check for no eligible quota changes Before this patch, function gfs2_quota_sync would always allocate a page full of memory and increment its quota sync generation number. This happened even when the system was completely idle or if no blocks were allocated or quota changes made. This patch adds function qd_changed to determine if any changes have been made that qualify for a quota sync. If not, it avoids the memory allocation and bumping the generation number, along with all the additional work it would do. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/quota.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 678d2e749db8..6f6e3493b5fa 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1294,6 +1294,24 @@ void gfs2_quota_change(struct gfs2_inode *ip, s64 change, } } +static bool qd_changed(struct gfs2_sbd *sdp) +{ + struct gfs2_quota_data *qd; + bool changed = false; + + spin_lock(&qd_lock); + list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { + if (test_bit(QDF_LOCKED, &qd->qd_flags) || + !test_bit(QDF_CHANGE, &qd->qd_flags)) + continue; + + changed = true; + break; + } + spin_unlock(&qd_lock); + return changed; +} + int gfs2_quota_sync(struct super_block *sb, int type) { struct gfs2_sbd *sdp = sb->s_fs_info; @@ -1303,6 +1321,9 @@ int gfs2_quota_sync(struct super_block *sb, int type) unsigned int x; int error = 0; + if (!qd_changed(sdp)) + return 0; + qda = kcalloc(max_qd, sizeof(struct gfs2_quota_data *), GFP_KERNEL); if (!qda) return -ENOMEM; From 0e072cac92d08c05257432b10fa8cd66d64b3f43 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 16 Jun 2023 13:22:04 -0500 Subject: [PATCH 49/51] gfs2: change qd_slot_count to qd_slot_ref Variable qd_slot_count is a reference count, not a count of slots. This patch renames it to qd_slot_ref to make that more clear. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/incore.h | 2 +- fs/gfs2/quota.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 7e94cf3562d6..a8c95c5293c6 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -452,7 +452,7 @@ struct gfs2_quota_data { s64 qd_change_sync; unsigned int qd_slot; - unsigned int qd_slot_count; + unsigned int qd_slot_ref; struct buffer_head *qd_bh; struct gfs2_quota_change *qd_bh_qc; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 6f6e3493b5fa..5160e5c36980 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -130,7 +130,7 @@ static void gfs2_qd_dispose(struct gfs2_quota_data *qd) if (!gfs2_withdrawn(sdp)) { gfs2_assert_warn(sdp, !qd->qd_change); - gfs2_assert_warn(sdp, !qd->qd_slot_count); + gfs2_assert_warn(sdp, !qd->qd_slot_ref); gfs2_assert_warn(sdp, !qd->qd_bh_count); } @@ -342,7 +342,7 @@ static int slot_get(struct gfs2_quota_data *qd) int error = 0; spin_lock(&sdp->sd_bitmap_lock); - if (qd->qd_slot_count == 0) { + if (qd->qd_slot_ref == 0) { bit = find_first_zero_bit(sdp->sd_quota_bitmap, sdp->sd_quota_slots); if (bit >= sdp->sd_quota_slots) { @@ -352,7 +352,7 @@ static int slot_get(struct gfs2_quota_data *qd) set_bit(bit, sdp->sd_quota_bitmap); qd->qd_slot = bit; } - qd->qd_slot_count++; + qd->qd_slot_ref++; out: spin_unlock(&sdp->sd_bitmap_lock); return error; @@ -363,8 +363,8 @@ static void slot_hold(struct gfs2_quota_data *qd) struct gfs2_sbd *sdp = qd->qd_sbd; spin_lock(&sdp->sd_bitmap_lock); - gfs2_assert(sdp, qd->qd_slot_count); - qd->qd_slot_count++; + gfs2_assert(sdp, qd->qd_slot_ref); + qd->qd_slot_ref++; spin_unlock(&sdp->sd_bitmap_lock); } @@ -373,8 +373,8 @@ static void slot_put(struct gfs2_quota_data *qd) struct gfs2_sbd *sdp = qd->qd_sbd; spin_lock(&sdp->sd_bitmap_lock); - gfs2_assert(sdp, qd->qd_slot_count); - if (!--qd->qd_slot_count) { + gfs2_assert(sdp, qd->qd_slot_ref); + if (!--qd->qd_slot_ref) { BUG_ON(!test_and_clear_bit(qd->qd_slot, sdp->sd_quota_bitmap)); qd->qd_slot = -1; } @@ -1446,7 +1446,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) set_bit(QDF_CHANGE, &qd->qd_flags); qd->qd_change = qc_change; qd->qd_slot = slot; - qd->qd_slot_count = 1; + qd->qd_slot_ref = 1; spin_lock(&qd_lock); BUG_ON(test_and_set_bit(slot, sdp->sd_quota_bitmap)); From 7b7e3ed78ef78f7e6a4d4f4a4973f6a5d22123ec Mon Sep 17 00:00:00 2001 From: Andrew Price Date: Thu, 31 Aug 2023 10:57:48 +0100 Subject: [PATCH 50/51] MAINTAINERS: Update gfs2 mailing list Signed-off-by: Andrew Price Signed-off-by: Andreas Gruenbacher --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0f966f05fb0d..6067875f01ae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8724,7 +8724,7 @@ F: scripts/get_maintainer.pl GFS2 FILE SYSTEM M: Bob Peterson M: Andreas Gruenbacher -L: cluster-devel@redhat.com +L: gfs2@lists.linux.dev S: Supported B: https://bugzilla.kernel.org/enter_bug.cgi?product=File%20System&component=gfs2 T: git git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2.git From 2938fd750e8b73a6dac4d9339fb6f7f1cd624a2d Mon Sep 17 00:00:00 2001 From: Andrew Price Date: Thu, 31 Aug 2023 10:57:49 +0100 Subject: [PATCH 51/51] MAINTAINERS: Update dlm mailing list The new gfs2@ list will also be used for dlm development. Signed-off-by: Andrew Price Acked-by: David Teigland Signed-off-by: Andreas Gruenbacher --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6067875f01ae..1131d849e1d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6092,7 +6092,7 @@ F: include/video/udlfb.h DISTRIBUTED LOCK MANAGER (DLM) M: Christine Caulfield M: David Teigland -L: cluster-devel@redhat.com +L: gfs2@lists.linux.dev S: Supported W: http://sources.redhat.com/cluster/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/teigland/linux-dlm.git