diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index cc60db6b9706..cbb44e280d4b 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -227,13 +227,22 @@ void ocfs2_recovery_disable_quota(struct ocfs2_super *osb) ocfs2_recovery_disable(osb, OCFS2_REC_QUOTA_WANT_DISABLE); } +/* Stop producers and drain completion work without freeing recovery state. */ +void ocfs2_recovery_stop(struct ocfs2_super *osb) +{ + ocfs2_recovery_disable(osb, OCFS2_REC_WANT_DISABLE); +} + 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); + /* + * Disable producers and drain work before freeing recovery state. + * Do this before setting vol_state to VOLUME_DISMOUNTED: completion + * work may still be waiting in __ocfs2_wait_on_mount(). + */ + ocfs2_recovery_stop(osb); /* * Now that recovery is shut down, and the osb is about to be diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 19fc920d26b1..4b52bf7b0938 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -147,6 +147,7 @@ void ocfs2_complete_recovery(struct work_struct *work); void ocfs2_wait_for_recovery(struct ocfs2_super *osb); int ocfs2_recovery_init(struct ocfs2_super *osb); +void ocfs2_recovery_stop(struct ocfs2_super *osb); void ocfs2_recovery_exit(struct ocfs2_super *osb); void ocfs2_recovery_disable_quota(struct ocfs2_super *osb); diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 0af2245ae303..3072b0386db8 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1831,6 +1831,9 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) /* Wait for worker to be done with the work structure in osb */ cancel_work_sync(&osb->dquot_drop_work); + /* Stop recovery producers before releasing their local allocators. */ + ocfs2_recovery_stop(osb); + ocfs2_shutdown_local_alloc(osb); ocfs2_truncate_log_shutdown(osb);