]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ocfs2: stop quota recovery before disabling quotas
authorJan Kara <jack@suse.cz>
Thu, 24 Apr 2025 13:45:13 +0000 (15:45 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 18 May 2025 06:24:08 +0000 (08:24 +0200)
commit fcaf3b2683b05a9684acdebda706a12025a6927a upstream.

Currently quota recovery is synchronized with unmount using sb->s_umount
semaphore.  That is however prone to deadlocks because
flush_workqueue(osb->ocfs2_wq) called from umount code can wait for quota
recovery to complete while ocfs2_finish_quota_recovery() waits for
sb->s_umount semaphore.

Grabbing of sb->s_umount semaphore in ocfs2_finish_quota_recovery() is
only needed to protect that function from disabling of quotas from
ocfs2_dismount_volume().  Handle this problem by disabling quota recovery
early during unmount in ocfs2_dismount_volume() instead so that we can
drop acquisition of sb->s_umount from ocfs2_finish_quota_recovery().

Link: https://lkml.kernel.org/r/20250424134515.18933-6-jack@suse.cz
Fixes: 5f530de63cfc ("ocfs2: Use s_umount for quota recovery protection")
Signed-off-by: Jan Kara <jack@suse.cz>
Reported-by: Shichangkuo <shi.changkuo@h3c.com>
Reported-by: Murad Masimov <m.masimov@mt-integration.ru>
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: <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/journal.h
fs/ocfs2/ocfs2.h
fs/ocfs2/quota_local.c
fs/ocfs2/super.c

index abaa3143c5c6d2426bbaeda90e5a461efa3d8465..b246e53271114d60cf87997691cd99f88c7453a7 100644 (file)
@@ -225,6 +225,11 @@ out_lock:
                flush_workqueue(osb->ocfs2_wq);
 }
 
+void ocfs2_recovery_disable_quota(struct ocfs2_super *osb)
+{
+       ocfs2_recovery_disable(osb, OCFS2_REC_QUOTA_WANT_DISABLE);
+}
+
 void ocfs2_recovery_exit(struct ocfs2_super *osb)
 {
        struct ocfs2_recovery_map *rm;
@@ -1489,6 +1494,18 @@ static int __ocfs2_recovery_thread(void *arg)
                }
        }
 restart:
+       if (quota_enabled) {
+               mutex_lock(&osb->recovery_lock);
+               /* Confirm that recovery thread will no longer recover quotas */
+               if (osb->recovery_state == OCFS2_REC_QUOTA_WANT_DISABLE) {
+                       osb->recovery_state = OCFS2_REC_QUOTA_DISABLED;
+                       wake_up(&osb->recovery_event);
+               }
+               if (osb->recovery_state >= OCFS2_REC_QUOTA_DISABLED)
+                       quota_enabled = 0;
+               mutex_unlock(&osb->recovery_lock);
+       }
+
        status = ocfs2_super_lock(osb, 1);
        if (status < 0) {
                mlog_errno(status);
@@ -1592,8 +1609,7 @@ bail:
 
        mutex_unlock(&osb->recovery_lock);
 
-       if (quota_enabled)
-               kfree(rm_quota);
+       kfree(rm_quota);
 
        return status;
 }
index e3c3a35dc5e0e79a839a9aab01c63551197e9d89..6397170f302f227945e987520b53c3f75c12d9aa 100644 (file)
@@ -148,6 +148,7 @@ void ocfs2_wait_for_recovery(struct ocfs2_super *osb);
 
 int ocfs2_recovery_init(struct ocfs2_super *osb);
 void ocfs2_recovery_exit(struct ocfs2_super *osb);
+void ocfs2_recovery_disable_quota(struct ocfs2_super *osb);
 
 int ocfs2_compute_replay_slots(struct ocfs2_super *osb);
 void ocfs2_free_replay_slots(struct ocfs2_super *osb);
index 9764a2904a7d6439831fbce33db8a920905d7664..5e3ebbab698ad1b5d473cc25268399783ff49521 100644 (file)
@@ -310,6 +310,12 @@ void ocfs2_initialize_journal_triggers(struct super_block *sb,
 
 enum ocfs2_recovery_state {
        OCFS2_REC_ENABLED = 0,
+       OCFS2_REC_QUOTA_WANT_DISABLE,
+       /*
+        * Must be OCFS2_REC_QUOTA_WANT_DISABLE + 1 for
+        * ocfs2_recovery_disable_quota() to work.
+        */
+       OCFS2_REC_QUOTA_DISABLED,
        OCFS2_REC_WANT_DISABLE,
        /*
         * Must be OCFS2_REC_WANT_DISABLE + 1 for ocfs2_recovery_exit() to work
index 4b4fa58cd32ff0bd0dd1eae1407bcc1e0580d1d8..0ca8975a1df479579b33c98cca1146761a61292c 100644 (file)
@@ -453,8 +453,7 @@ out:
 
 /* Sync changes in local quota file into global quota file and
  * reinitialize local quota file.
- * The function expects local quota file to be already locked and
- * s_umount locked in shared mode. */
+ * The function expects local quota file to be already locked. */
 static int ocfs2_recover_local_quota_file(struct inode *lqinode,
                                          int type,
                                          struct ocfs2_quota_recovery *rec)
@@ -585,7 +584,6 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
 {
        unsigned int ino[OCFS2_MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
                                              LOCAL_GROUP_QUOTA_SYSTEM_INODE };
-       struct super_block *sb = osb->sb;
        struct ocfs2_local_disk_dqinfo *ldinfo;
        struct buffer_head *bh;
        handle_t *handle;
@@ -597,7 +595,6 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
        printk(KERN_NOTICE "ocfs2: Finishing quota recovery on device (%s) for "
               "slot %u\n", osb->dev_str, slot_num);
 
-       down_read(&sb->s_umount);
        for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
                if (list_empty(&(rec->r_list[type])))
                        continue;
@@ -674,7 +671,6 @@ out_put:
                        break;
        }
 out:
-       up_read(&sb->s_umount);
        kfree(rec);
        return status;
 }
@@ -840,8 +836,7 @@ static int ocfs2_local_free_info(struct super_block *sb, int type)
        ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
 
        /*
-        * s_umount held in exclusive mode protects us against racing with
-        * recovery thread...
+        * ocfs2_dismount_volume() has already aborted quota recovery...
         */
        if (oinfo->dqi_rec) {
                ocfs2_free_quota_recovery(oinfo->dqi_rec);
index 84fa585c6513a59cbe18468cea155baf4d5a2962..e585e77cdc88e1276b4e83979f6b7ec39c012c71 100644 (file)
@@ -1870,6 +1870,9 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
        /* Orphan scan should be stopped as early as possible */
        ocfs2_orphan_scan_stop(osb);
 
+       /* Stop quota recovery so that we can disable quotas */
+       ocfs2_recovery_disable_quota(osb);
+
        ocfs2_disable_quotas(osb);
 
        /* All dquots should be freed by now */