| From: Jan Kara <jack@suse.cz> |
| Subject: ocfs2: implement handshaking with ocfs2 recovery thread |
| Date: Thu, 24 Apr 2025 15:45:12 +0200 |
| |
| 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> |
| --- |
| |
| fs/ocfs2/journal.c | 52 ++++++++++++++++++++++++++++--------------- |
| fs/ocfs2/ocfs2.h | 4 +++ |
| 2 files changed, 39 insertions(+), 17 deletions(-) |
| |
| --- a/fs/ocfs2/journal.c~ocfs2-implement-handshaking-with-ocfs2-recovery-thread |
| +++ a/fs/ocfs2/journal.c |
| @@ -190,31 +190,48 @@ int ocfs2_recovery_init(struct ocfs2_sup |
| 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 |
| @@ -1569,7 +1586,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); |
| @@ -1585,13 +1603,13 @@ void ocfs2_recovery_thread(struct ocfs2_ |
| 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) |
| --- a/fs/ocfs2/ocfs2.h~ocfs2-implement-handshaking-with-ocfs2-recovery-thread |
| +++ a/fs/ocfs2/ocfs2.h |
| @@ -310,6 +310,10 @@ void ocfs2_initialize_journal_triggers(s |
| |
| 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, |
| }; |
| |
| _ |