From 4b0d18b5542247139fa1a541a19f2ff8705cd75a Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 4 Apr 2024 20:01:37 +0200 Subject: [PATCH 1/5] gfs2: Enable automatic glock hash table shrinking All of the examiner functions passed to glock_hash_walk() can deal with glock hash table resizes and the resulting repeat visiting of glocks, so we can allow automatic glock hash table shrinking. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index b8a144d3a73b..0a93824424d5 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -78,6 +78,7 @@ static const struct rhashtable_params ht_parms = { .key_len = offsetofend(struct lm_lockname, ln_type), .key_offset = offsetof(struct gfs2_glock, gl_name), .head_offset = offsetof(struct gfs2_glock, gl_node), + .automatic_shrinking = true, }; static struct rhashtable gl_hash_table; From 864174f976474bfa7b787ec654733febe3b03f84 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 7 May 2024 13:48:59 +0200 Subject: [PATCH 2/5] gfs2: Don't cache unreferenced glocks Currently, gfs2 caches unreferenced glocks until memory pressure sets in or the filesystem is unmounted. This was supposedly done to avoid excessive log flushing: when a glock still has outstanding revokes, freeing it requires an extra log flush, and we want to avoid too many of those extra log flushes. Since commit 9287c6452d2b1 ("gfs2: Fix occasional glock use-after-free"), outstanding revokes are accounted for in the glock reference count and glocks with outstanding revokes will never be freed anymore, so this is no longer an issue. This also means that we won't need a glock LRU list anymore, but we leave removing that list to a later patch for better readability. It might seem that glocks that are not referenced anymore can be dropped immediately without unlocking them first, but that isn't true for inode glocks that have an address space attached (the "gfs2_glock(aspace)" slab cache): that address space is only truncated when the associated glock is unlocked. So unlock those glocks when they become unreferenced. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 0a93824424d5..f00dcae426c5 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -258,8 +258,8 @@ static bool __gfs2_glock_put_or_lock(struct gfs2_glock *gl) return true; GLOCK_BUG_ON(gl, gl->gl_lockref.count != 1); if (gl->gl_state != LM_ST_UNLOCKED) { - gl->gl_lockref.count--; - gfs2_glock_add_to_lru(gl); + request_demote(gl, LM_ST_UNLOCKED, 0, false); + gfs2_glock_queue_work(gl, 0); spin_unlock(&gl->gl_lockref.lock); return true; } @@ -983,16 +983,19 @@ static void delete_work_func(struct work_struct *work) static void glock_work_func(struct work_struct *work) { - unsigned long delay = 0; struct gfs2_glock *gl = container_of(work, struct gfs2_glock, gl_work.work); - unsigned int drop_refs = 1; + unsigned int drop_refs; + unsigned long delay; spin_lock(&gl->gl_lockref.lock); +again: + drop_refs = 1; if (test_bit(GLF_HAVE_REPLY, &gl->gl_flags)) { clear_bit(GLF_HAVE_REPLY, &gl->gl_flags); finish_xmote(gl, gl->gl_reply); drop_refs++; } + delay = 0; if (test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) && gl->gl_state != LM_ST_UNLOCKED && gl->gl_demote_state != LM_ST_EXCLUSIVE) { @@ -1020,11 +1023,13 @@ static void glock_work_func(struct work_struct *work) GLOCK_BUG_ON(gl, gl->gl_lockref.count < drop_refs); gl->gl_lockref.count -= drop_refs; if (!gl->gl_lockref.count) { - if (gl->gl_state == LM_ST_UNLOCKED) { - __gfs2_glock_put(gl); - return; + if (gl->gl_state != LM_ST_UNLOCKED) { + gl->gl_lockref.count++; + request_demote(gl, LM_ST_UNLOCKED, 0, false); + goto again; } - gfs2_glock_add_to_lru(gl); + __gfs2_glock_put(gl); + return; } spin_unlock(&gl->gl_lockref.lock); } From c6963f393a12e6a68de1a5af8e97a02f87bea583 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 8 May 2024 11:57:08 +0200 Subject: [PATCH 3/5] gfs2: Skip dlm unlocks earlier When we were still caching unreferenced glocks, evicting all the cached inodes in gfs2_kill_sb() did put the unreferenced glocks onto the glock lru list, and they would be freed in gfs2_gl_hash_clear(). We could set the SDF_SKIP_DLM_UNLOCK flag in gfs2_gl_hash_clear() to indicate to gdlm_put_lock() to skip unlocking glocks explicitly when possible. Now, glocks are demoted and dropped immediately though, so to allow gdlm_put_lock() to skip unnecessary unlocking, we need to set some "unmount" flag before calling gfs2_evict_inodes(). We can use the existing SDF_KILL flag for that if we set it before calling gfs2_evict_inodes() in gfs2_kill_sb(). Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 2 -- fs/gfs2/incore.h | 1 - fs/gfs2/lock_dlm.c | 2 +- fs/gfs2/ops_fstype.c | 2 +- fs/gfs2/sys.c | 2 -- 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index f00dcae426c5..eee1f31a41f0 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -2152,8 +2152,6 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp) unsigned long start = jiffies; bool timed_out = false; - set_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags); - flush_workqueue(sdp->sd_glock_wq); glock_hash_walk(clear_glock, sdp); flush_workqueue(sdp->sd_glock_wq); diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 61465777826a..6098447866d9 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -601,7 +601,6 @@ enum { SDF_DEMOTE = 5, SDF_NOJOURNALID = 6, SDF_RORECOVERY = 7, /* read only recovery */ - SDF_SKIP_DLM_UNLOCK = 8, SDF_FORCE_AIL_FLUSH = 9, SDF_FREEZE_INITIATOR = 10, SDF_KILL = 15, diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 7828ad0b6f5a..ab7ac8e634bf 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -347,7 +347,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl) * DLM_LOCK_PW mode, the lock value block (LVB) would be lost. */ - if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) && + if (test_bit(SDF_KILL, &sdp->sd_flags) && (!gl->gl_lksb.sb_lvbptr || gl->gl_state != LM_ST_EXCLUSIVE)) { gfs2_glock_free_later(gl); return; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 9b9e70f14d25..71e9dedd8b88 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1783,6 +1783,7 @@ static void gfs2_kill_sb(struct super_block *sb) sdp->sd_master_dir = NULL; shrink_dcache_sb(sb); + set_bit(SDF_KILL, &sdp->sd_flags); gfs2_evict_inodes(sb); /* @@ -1790,7 +1791,6 @@ static void gfs2_kill_sb(struct super_block *sb) * destroy_workqueue()) to ensure that any delete work that * may be running will also see the SDF_KILL flag. */ - set_bit(SDF_KILL, &sdp->sd_flags); gfs2_flush_delete_work(sdp); destroy_workqueue(sdp->sd_delete_wq); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 7051db9dbea0..ea2c7b9e4a77 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -80,7 +80,6 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) "No Journal ID: %d\n" "Mounted RO: %d\n" "RO Recovery: %d\n" - "Skip DLM Unlock: %d\n" "Force AIL Flush: %d\n" "FS Freeze Initiator: %d\n" "FS Frozen: %d\n" @@ -109,7 +108,6 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) test_bit(SDF_NOJOURNALID, &f), (sb_rdonly(sdp->sd_vfs) ? 1 : 0), test_bit(SDF_RORECOVERY, &f), - test_bit(SDF_SKIP_DLM_UNLOCK, &f), test_bit(SDF_FORCE_AIL_FLUSH, &f), test_bit(SDF_FREEZE_INITIATOR, &f), test_bit(SDF_FROZEN, &f), From da26828b82a450f462bacbd51b10d1f71ee92630 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 8 May 2024 03:35:36 +0200 Subject: [PATCH 4/5] gfs2: Remove the glock lru list and shrinker We are no longer keeping unreferences glocks around, so remove the now-obsolete glock lru list and shrinker. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 201 ++----------------------------------------- fs/gfs2/glock.h | 2 +- fs/gfs2/incore.h | 6 +- fs/gfs2/main.c | 1 - fs/gfs2/ops_fstype.c | 3 +- fs/gfs2/super.c | 2 +- fs/gfs2/trace_gfs2.h | 1 - 7 files changed, 15 insertions(+), 201 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index eee1f31a41f0..558c8c660a3d 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -66,9 +66,6 @@ static void request_demote(struct gfs2_glock *gl, unsigned int state, unsigned long delay, bool remote); static struct dentry *gfs2_root; -static LIST_HEAD(lru_list); -static atomic_t lru_count = ATOMIC_INIT(0); -static DEFINE_SPINLOCK(lru_lock); #define GFS2_GL_HASH_SHIFT 15 #define GFS2_GL_HASH_SIZE BIT(GFS2_GL_HASH_SHIFT) @@ -158,9 +155,9 @@ void gfs2_glock_free(struct gfs2_glock *gl) { void gfs2_glock_free_later(struct gfs2_glock *gl) { struct gfs2_sbd *sdp = glock_sbd(gl); - spin_lock(&lru_lock); - list_add(&gl->gl_lru, &sdp->sd_dead_glocks); - spin_unlock(&lru_lock); + spin_lock(&sdp->sd_dead_lock); + list_add(&gl->gl_dead, &sdp->sd_dead_glocks); + spin_unlock(&sdp->sd_dead_lock); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) wake_up(&sdp->sd_kill_wait); } @@ -172,8 +169,8 @@ static void gfs2_free_dead_glocks(struct gfs2_sbd *sdp) while(!list_empty(list)) { struct gfs2_glock *gl; - gl = list_first_entry(list, struct gfs2_glock, gl_lru); - list_del_init(&gl->gl_lru); + gl = list_first_entry(list, struct gfs2_glock, gl_dead); + list_del(&gl->gl_dead); __gfs2_glock_free(gl); } } @@ -191,30 +188,6 @@ struct gfs2_glock *gfs2_glock_hold(struct gfs2_glock *gl) return gl; } -static void gfs2_glock_add_to_lru(struct gfs2_glock *gl) -{ - spin_lock(&lru_lock); - list_move_tail(&gl->gl_lru, &lru_list); - - if (!test_bit(GLF_LRU, &gl->gl_flags)) { - set_bit(GLF_LRU, &gl->gl_flags); - atomic_inc(&lru_count); - } - - spin_unlock(&lru_lock); -} - -static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl) -{ - spin_lock(&lru_lock); - if (test_bit(GLF_LRU, &gl->gl_flags)) { - list_del_init(&gl->gl_lru); - atomic_dec(&lru_count); - clear_bit(GLF_LRU, &gl->gl_flags); - } - spin_unlock(&lru_lock); -} - /* * Enqueue the glock on the work queue. Passes one glock reference on to the * work queue. @@ -241,7 +214,6 @@ static void __gfs2_glock_put(struct gfs2_glock *gl) lockref_mark_dead(&gl->gl_lockref); spin_unlock(&gl->gl_lockref.lock); - gfs2_glock_remove_from_lru(gl); GLOCK_BUG_ON(gl, !list_empty(&gl->gl_holders)); if (mapping) { truncate_inode_pages_final(mapping); @@ -1065,8 +1037,6 @@ static struct gfs2_glock *find_insert_glock(struct lm_lockname *name, out: rcu_read_unlock(); finish_wait(wq, &wait.wait); - if (gl) - gfs2_glock_remove_from_lru(gl); return gl; } @@ -1879,125 +1849,6 @@ void gfs2_glock_complete(struct gfs2_glock *gl, int ret) spin_unlock(&gl->gl_lockref.lock); } -static int glock_cmp(void *priv, const struct list_head *a, - const struct list_head *b) -{ - struct gfs2_glock *gla, *glb; - - gla = list_entry(a, struct gfs2_glock, gl_lru); - glb = list_entry(b, struct gfs2_glock, gl_lru); - - if (glock_number(gla) > glock_number(glb)) - return 1; - if (glock_number(gla) < glock_number(glb)) - return -1; - - return 0; -} - -static bool can_free_glock(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = glock_sbd(gl); - - return !test_bit(GLF_LOCK, &gl->gl_flags) && - !gl->gl_lockref.count && - (!test_bit(GLF_LFLUSH, &gl->gl_flags) || - test_bit(SDF_KILL, &sdp->sd_flags)); -} - -/** - * gfs2_dispose_glock_lru - Demote a list of glocks - * @list: The list to dispose of - * - * Disposing of glocks may involve disk accesses, so that here we sort - * the glocks by number (i.e. disk location of the inodes) so that if - * there are any such accesses, they'll be sent in order (mostly). - * - * Must be called under the lru_lock, but may drop and retake this - * lock. While the lru_lock is dropped, entries may vanish from the - * list, but no new entries will appear on the list (since it is - * private) - */ - -static unsigned long gfs2_dispose_glock_lru(struct list_head *list) -__releases(&lru_lock) -__acquires(&lru_lock) -{ - struct gfs2_glock *gl; - unsigned long freed = 0; - - list_sort(NULL, list, glock_cmp); - - while(!list_empty(list)) { - gl = list_first_entry(list, struct gfs2_glock, gl_lru); - if (!spin_trylock(&gl->gl_lockref.lock)) { -add_back_to_lru: - list_move(&gl->gl_lru, &lru_list); - continue; - } - if (!can_free_glock(gl)) { - spin_unlock(&gl->gl_lockref.lock); - goto add_back_to_lru; - } - list_del_init(&gl->gl_lru); - atomic_dec(&lru_count); - clear_bit(GLF_LRU, &gl->gl_flags); - freed++; - gl->gl_lockref.count++; - if (gl->gl_state != LM_ST_UNLOCKED) - request_demote(gl, LM_ST_UNLOCKED, 0, false); - gfs2_glock_queue_work(gl, 0); - spin_unlock(&gl->gl_lockref.lock); - cond_resched_lock(&lru_lock); - } - return freed; -} - -/** - * gfs2_scan_glock_lru - Scan the LRU looking for locks to demote - * @nr: The number of entries to scan - * - * This function selects the entries on the LRU which are able to - * be demoted, and then kicks off the process by calling - * gfs2_dispose_glock_lru() above. - */ - -static unsigned long gfs2_scan_glock_lru(unsigned long nr) -{ - struct gfs2_glock *gl, *next; - LIST_HEAD(dispose); - unsigned long freed = 0; - - spin_lock(&lru_lock); - list_for_each_entry_safe(gl, next, &lru_list, gl_lru) { - if (!nr--) - break; - if (can_free_glock(gl)) - list_move(&gl->gl_lru, &dispose); - } - if (!list_empty(&dispose)) - freed = gfs2_dispose_glock_lru(&dispose); - spin_unlock(&lru_lock); - - return freed; -} - -static unsigned long gfs2_glock_shrink_scan(struct shrinker *shrink, - struct shrink_control *sc) -{ - if (!(sc->gfp_mask & __GFP_FS)) - return SHRINK_STOP; - return gfs2_scan_glock_lru(sc->nr_to_scan); -} - -static unsigned long gfs2_glock_shrink_count(struct shrinker *shrink, - struct shrink_control *sc) -{ - return vfs_pressure_ratio(atomic_read(&lru_count)); -} - -static struct shrinker *glock_shrinker; - /** * glock_hash_walk - Call a function for glock in a hash bucket * @examiner: the function @@ -2068,33 +1919,12 @@ static void thaw_glock(struct gfs2_glock *gl) if (!lockref_get_not_dead(&gl->gl_lockref)) return; - gfs2_glock_remove_from_lru(gl); spin_lock(&gl->gl_lockref.lock); set_bit(GLF_HAVE_REPLY, &gl->gl_flags); gfs2_glock_queue_work(gl, 0); spin_unlock(&gl->gl_lockref.lock); } -/** - * clear_glock - look at a glock and see if we can free it from glock cache - * @gl: the glock to look at - * - */ - -static void clear_glock(struct gfs2_glock *gl) -{ - gfs2_glock_remove_from_lru(gl); - - spin_lock(&gl->gl_lockref.lock); - if (!__lockref_is_dead(&gl->gl_lockref)) { - gl->gl_lockref.count++; - if (gl->gl_state != LM_ST_UNLOCKED) - request_demote(gl, LM_ST_UNLOCKED, 0, false); - gfs2_glock_queue_work(gl, 0); - } - spin_unlock(&gl->gl_lockref.lock); -} - /** * gfs2_glock_thaw - Thaw any frozen glocks * @sdp: The super block @@ -2141,20 +1971,17 @@ void gfs2_withdraw_glocks(struct gfs2_sbd *sdp) } /** - * gfs2_gl_hash_clear - Empty out the glock hash table + * gfs2_wait_glocks - Wait for the remaining glocks to go away * @sdp: the filesystem * * Called when unmounting the filesystem. */ -void gfs2_gl_hash_clear(struct gfs2_sbd *sdp) +void gfs2_wait_glocks(struct gfs2_sbd *sdp) { unsigned long start = jiffies; bool timed_out = false; - glock_hash_walk(clear_glock, sdp); - flush_workqueue(sdp->sd_glock_wq); - while (!timed_out) { wait_event_timeout(sdp->sd_kill_wait, !atomic_read(&sdp->sd_glock_disposal), @@ -2277,8 +2104,6 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl) *p++ = 'F'; if (!list_empty(&gl->gl_holders)) *p++ = 'q'; - if (test_bit(GLF_LRU, gflags)) - *p++ = 'L'; if (gl->gl_object) *p++ = 'o'; if (test_bit(GLF_BLOCKING, gflags)) @@ -2438,17 +2263,6 @@ int __init gfs2_glock_init(void) if (ret < 0) return ret; - glock_shrinker = shrinker_alloc(0, "gfs2-glock"); - if (!glock_shrinker) { - rhashtable_destroy(&gl_hash_table); - return -ENOMEM; - } - - glock_shrinker->count_objects = gfs2_glock_shrink_count; - glock_shrinker->scan_objects = gfs2_glock_shrink_scan; - - shrinker_register(glock_shrinker); - for (i = 0; i < GLOCK_WAIT_TABLE_SIZE; i++) init_waitqueue_head(glock_wait_table + i); @@ -2457,7 +2271,6 @@ int __init gfs2_glock_init(void) void gfs2_glock_exit(void) { - shrinker_free(glock_shrinker); rhashtable_destroy(&gl_hash_table); } diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 6341ac9b863f..8b9e22befefb 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -263,7 +263,7 @@ bool gfs2_queue_try_to_evict(struct gfs2_glock *gl); bool gfs2_queue_verify_delete(struct gfs2_glock *gl, bool later); void gfs2_cancel_delete_work(struct gfs2_glock *gl); void gfs2_flush_delete_work(struct gfs2_sbd *sdp); -void gfs2_gl_hash_clear(struct gfs2_sbd *sdp); +void gfs2_wait_glocks(struct gfs2_sbd *sdp); void gfs2_withdraw_glocks(struct gfs2_sbd *sdp); void gfs2_glock_thaw(struct gfs2_sbd *sdp); void gfs2_glock_free(struct gfs2_glock *gl); diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 6098447866d9..dadb4d3c9d3d 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -321,7 +321,6 @@ enum { GLF_INITIAL = 10, GLF_HAVE_FROZEN_REPLY = 11, GLF_INSTANTIATE_IN_PROG = 12, /* instantiate happening now */ - GLF_LRU = 13, GLF_OBJECT = 14, /* Used only for tracing */ GLF_BLOCKING = 15, GLF_TRY_TO_EVICT = 17, /* iopen glocks only */ @@ -355,7 +354,7 @@ struct gfs2_glock { unsigned long gl_tchange; void *gl_object; - struct list_head gl_lru; + struct list_head gl_dead; struct list_head gl_ail_list; atomic_t gl_ail_count; atomic_t gl_revokes; @@ -833,6 +832,9 @@ struct gfs2_sbd { struct list_head sd_ail1_list; struct list_head sd_ail2_list; + /* glocks */ + spinlock_t sd_dead_lock; + /* For quiescing the filesystem */ struct gfs2_holder sd_freeze_gh; struct mutex sd_freeze_mutex; diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index 9d65719353fa..36c9c06e91ac 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -52,7 +52,6 @@ static void gfs2_init_glock_once(void *foo) struct gfs2_glock *gl = foo; INIT_LIST_HEAD(&gl->gl_holders); - INIT_LIST_HEAD(&gl->gl_lru); INIT_LIST_HEAD(&gl->gl_ail_list); atomic_set(&gl->gl_ail_count, 0); atomic_set(&gl->gl_revokes, 0); diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 71e9dedd8b88..718e0da7dfce 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -123,6 +123,8 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) INIT_LIST_HEAD(&sdp->sd_ail1_list); INIT_LIST_HEAD(&sdp->sd_ail2_list); + spin_lock_init(&sdp->sd_dead_lock); + init_rwsem(&sdp->sd_log_flush_lock); atomic_set(&sdp->sd_log_in_flight, 0); init_waitqueue_head(&sdp->sd_log_flush_wait); @@ -1300,7 +1302,6 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) init_locking(sdp, &mount_gh, UNDO); fail_lm: complete_all(&sdp->sd_journal_ready); - gfs2_gl_hash_clear(sdp); gfs2_lm_unmount(sdp); fail_debug: gfs2_delete_debugfs_file(sdp); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 4d854556b529..06302c29340f 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -638,7 +638,7 @@ static void gfs2_put_super(struct super_block *sb) gfs2_clear_rgrpd(sdp); gfs2_jindex_free(sdp); /* Take apart glock structures and buffer lists */ - gfs2_gl_hash_clear(sdp); + gfs2_wait_glocks(sdp); iput(sdp->sd_inode); gfs2_delete_debugfs_file(sdp); diff --git a/fs/gfs2/trace_gfs2.h b/fs/gfs2/trace_gfs2.h index 6fd39fcdd00e..bc40320ef239 100644 --- a/fs/gfs2/trace_gfs2.h +++ b/fs/gfs2/trace_gfs2.h @@ -56,7 +56,6 @@ {(1UL << GLF_HAVE_REPLY), "r" }, \ {(1UL << GLF_INITIAL), "a" }, \ {(1UL << GLF_HAVE_FROZEN_REPLY), "F" }, \ - {(1UL << GLF_LRU), "L" }, \ {(1UL << GLF_OBJECT), "o" }, \ {(1UL << GLF_BLOCKING), "b" }, \ {(1UL << GLF_INSTANTIATE_NEEDED), "n" }, \ From 657e5af4fe0b9696a6c7232da1727a9f37eaa04d Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 12 Aug 2026 12:08:06 +0200 Subject: [PATCH 5/5] gfs2: harden gfs2_glock_hold Function gfs2_glock_hold() is expected only to be called when the glock is held, so use lockref_get_not_zero() instead of lockref_get_not_dead(). In addition, when an asynchronous callback arrives in gfs2_glock_cb(), the glock can already be dead (from __gfs2_glock_put()), or it can be on the glock lru list with refcount 0, so we cannot use gfs2_glock_hold() there. With gfs2_glock_cb() now handling dead glocks, we can remove the racy check in gdlm_bast(). Signed-off-by: Andreas Gruenbacher --- fs/gfs2/glock.c | 6 ++++-- fs/gfs2/lock_dlm.c | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 558c8c660a3d..9cf59ee2f443 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -183,7 +183,7 @@ static void gfs2_free_dead_glocks(struct gfs2_sbd *sdp) struct gfs2_glock *gfs2_glock_hold(struct gfs2_glock *gl) { - if (!lockref_get_not_dead(&gl->gl_lockref)) + if (!lockref_get_not_zero(&gl->gl_lockref)) GLOCK_BUG_ON(gl, 1); return gl; } @@ -1769,7 +1769,9 @@ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state) { unsigned long delay = 0; - gfs2_glock_hold(gl); + if (!lockref_get_not_dead(&gl->gl_lockref)) + return; + spin_lock(&gl->gl_lockref.lock); if (!list_empty(&gl->gl_holders) && glock_type(gl) == LM_TYPE_INODE) { diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index ab7ac8e634bf..babce07b84f1 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -182,9 +182,6 @@ static void gdlm_bast(void *arg, int mode) { struct gfs2_glock *gl = arg; - if (__lockref_is_dead(&gl->gl_lockref)) - return; - switch (mode) { case DLM_LOCK_EX: gfs2_glock_cb(gl, LM_ST_UNLOCKED);