mirror of
https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable.git
synced 2025-11-05 08:07:50 +10:00
ocfs2: stop quota recovery before disabling quotas
commitfcaf3b2683upstream. 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:5f530de63c("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>
This commit is contained in:
parent
ca68863a96
commit
073a7db01d
@ -229,6 +229,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;
|
||||
@ -1408,6 +1413,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);
|
||||
@ -1511,8 +1528,7 @@ bail:
|
||||
|
||||
mutex_unlock(&osb->recovery_lock);
|
||||
|
||||
if (quota_enabled)
|
||||
kfree(rm_quota);
|
||||
kfree(rm_quota);
|
||||
|
||||
/* no one is callint kthread_stop() for us so the kthread() api
|
||||
* requires that we call do_exit(). And it isn't exported, but
|
||||
|
||||
@ -150,6 +150,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);
|
||||
|
||||
@ -288,6 +288,12 @@ enum ocfs2_mount_options
|
||||
|
||||
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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1876,6 +1876,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 */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user