]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ocfs2: implement handshaking with ocfs2 recovery thread
authorJan Kara <jack@suse.cz>
Thu, 24 Apr 2025 13:45:12 +0000 (15:45 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 18 May 2025 06:20:37 +0000 (08:20 +0200)
commit 8f947e0fd595951460f5a6e1ac29baa82fa02eab upstream.

We will need ocfs2 recovery thread to acknowledge transitions of
recovery_state when disabling particular types of recovery.  This is
similar to what currently happens when disabling recovery completely, just
more general.  Implement the handshake and use it for exit from recovery.

Link: https://lkml.kernel.org/r/20250424134515.18933-5-jack@suse.cz
Fixes: 5f530de63cfc ("ocfs2: Use s_umount for quota recovery protection")
Signed-off-by: Jan Kara <jack@suse.cz>
Reviewed-by: Heming Zhao <heming.zhao@suse.com>
Tested-by: Heming Zhao <heming.zhao@suse.com>
Acked-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Jun Piao <piaojun@huawei.com>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Murad Masimov <m.masimov@mt-integration.ru>
Cc: Shichangkuo <shi.changkuo@h3c.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ocfs2/journal.c
fs/ocfs2/ocfs2.h

index e932917cfc93489510a8f67a0a5b70ae6214e0cd..cdfb47eeb685df019b83926dec0b9d9944932e1a 100644 (file)
@@ -192,31 +192,48 @@ int ocfs2_recovery_init(struct ocfs2_super *osb)
        return 0;
 }
 
-/* we can't grab the goofy sem lock from inside wait_event, so we use
- * memory barriers to make sure that we'll see the null task before
- * being woken up */
 static int ocfs2_recovery_thread_running(struct ocfs2_super *osb)
 {
-       mb();
        return osb->recovery_thread_task != NULL;
 }
 
-void ocfs2_recovery_exit(struct ocfs2_super *osb)
+static void ocfs2_recovery_disable(struct ocfs2_super *osb,
+                                  enum ocfs2_recovery_state state)
 {
-       struct ocfs2_recovery_map *rm;
-
-       /* disable any new recovery threads and wait for any currently
-        * running ones to exit. Do this before setting the vol_state. */
        mutex_lock(&osb->recovery_lock);
-       osb->recovery_state = OCFS2_REC_DISABLED;
+       /*
+        * If recovery thread is not running, we can directly transition to
+        * final state.
+        */
+       if (!ocfs2_recovery_thread_running(osb)) {
+               osb->recovery_state = state + 1;
+               goto out_lock;
+       }
+       osb->recovery_state = state;
+       /* Wait for recovery thread to acknowledge state transition */
+       wait_event_cmd(osb->recovery_event,
+                      !ocfs2_recovery_thread_running(osb) ||
+                               osb->recovery_state >= state + 1,
+                      mutex_unlock(&osb->recovery_lock),
+                      mutex_lock(&osb->recovery_lock));
+out_lock:
        mutex_unlock(&osb->recovery_lock);
-       wait_event(osb->recovery_event, !ocfs2_recovery_thread_running(osb));
 
-       /* At this point, we know that no more recovery threads can be
-        * launched, so wait for any recovery completion work to
-        * complete. */
+       /*
+        * At this point we know that no more recovery work can be queued so
+        * wait for any recovery completion work to complete.
+        */
        if (osb->ocfs2_wq)
                flush_workqueue(osb->ocfs2_wq);
+}
+
+void ocfs2_recovery_exit(struct ocfs2_super *osb)
+{
+       struct ocfs2_recovery_map *rm;
+
+       /* disable any new recovery threads and wait for any currently
+        * running ones to exit. Do this before setting the vol_state. */
+       ocfs2_recovery_disable(osb, OCFS2_REC_WANT_DISABLE);
 
        /*
         * Now that recovery is shut down, and the osb is about to be
@@ -1507,7 +1524,8 @@ bail:
 
        ocfs2_free_replay_slots(osb);
        osb->recovery_thread_task = NULL;
-       mb(); /* sync with ocfs2_recovery_thread_running */
+       if (osb->recovery_state == OCFS2_REC_WANT_DISABLE)
+               osb->recovery_state = OCFS2_REC_DISABLED;
        wake_up(&osb->recovery_event);
 
        mutex_unlock(&osb->recovery_lock);
@@ -1526,13 +1544,13 @@ void ocfs2_recovery_thread(struct ocfs2_super *osb, int node_num)
        int was_set = -1;
 
        mutex_lock(&osb->recovery_lock);
-       if (osb->recovery_state < OCFS2_REC_DISABLED)
+       if (osb->recovery_state < OCFS2_REC_WANT_DISABLE)
                was_set = ocfs2_recovery_map_set(osb, node_num);
 
        trace_ocfs2_recovery_thread(node_num, osb->node_num,
                osb->recovery_state, osb->recovery_thread_task, was_set);
 
-       if (osb->recovery_state == OCFS2_REC_DISABLED)
+       if (osb->recovery_state >= OCFS2_REC_WANT_DISABLE)
                goto out;
 
        if (osb->recovery_thread_task)
index d052c31da15216aa5eeb9ba843b79724b2e5fd9c..3a577ad0e69500942c5e2472409feb20b073a90d 100644 (file)
@@ -286,6 +286,10 @@ enum ocfs2_mount_options
 
 enum ocfs2_recovery_state {
        OCFS2_REC_ENABLED = 0,
+       OCFS2_REC_WANT_DISABLE,
+       /*
+        * Must be OCFS2_REC_WANT_DISABLE + 1 for ocfs2_recovery_exit() to work
+        */
        OCFS2_REC_DISABLED,
 };