Skip to content

Commit

Permalink
ocfs2: stop quota recovery before disabling quotas
Browse files Browse the repository at this point in the history
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: 5f530de ("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>
  • Loading branch information
Jan Kara authored and Andrew Morton committed May 8, 2025
1 parent 8f947e0 commit fcaf3b2
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 9 deletions.
20 changes: 18 additions & 2 deletions fs/ocfs2/journal.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ static void ocfs2_recovery_disable(struct ocfs2_super *osb,
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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1592,8 +1609,7 @@ static int __ocfs2_recovery_thread(void *arg)

mutex_unlock(&osb->recovery_lock);

if (quota_enabled)
kfree(rm_quota);
kfree(rm_quota);

return status;
}
Expand Down
1 change: 1 addition & 0 deletions fs/ocfs2/journal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions fs/ocfs2/ocfs2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 2 additions & 7 deletions fs/ocfs2/quota_local.c
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,7 @@ struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(

/* 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)
Expand Down Expand Up @@ -588,7 +587,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;
Expand All @@ -600,7 +598,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;
Expand Down Expand Up @@ -677,7 +674,6 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
break;
}
out:
up_read(&sb->s_umount);
kfree(rec);
return status;
}
Expand Down Expand Up @@ -843,8 +839,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);
Expand Down
3 changes: 3 additions & 0 deletions fs/ocfs2/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -1812,6 +1812,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 */
Expand Down

0 comments on commit fcaf3b2

Please sign in to comment.