Skip to content

Commit fcaf3b2

Browse files
jankaraakpm00
authored andcommitted
ocfs2: stop quota recovery before disabling quotas
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/[email protected] Fixes: 5f530de ("ocfs2: Use s_umount for quota recovery protection") Signed-off-by: Jan Kara <[email protected]> Reported-by: Shichangkuo <[email protected]> Reported-by: Murad Masimov <[email protected]> Reviewed-by: Heming Zhao <[email protected]> Tested-by: Heming Zhao <[email protected]> Acked-by: Joseph Qi <[email protected]> Cc: Changwei Ge <[email protected]> Cc: Joel Becker <[email protected]> Cc: Jun Piao <[email protected]> Cc: Junxiao Bi <[email protected]> Cc: Mark Fasheh <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 8f947e0 commit fcaf3b2

File tree

5 files changed

+30
-9
lines changed

5 files changed

+30
-9
lines changed

fs/ocfs2/journal.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ static void ocfs2_recovery_disable(struct ocfs2_super *osb,
225225
flush_workqueue(osb->ocfs2_wq);
226226
}
227227

228+
void ocfs2_recovery_disable_quota(struct ocfs2_super *osb)
229+
{
230+
ocfs2_recovery_disable(osb, OCFS2_REC_QUOTA_WANT_DISABLE);
231+
}
232+
228233
void ocfs2_recovery_exit(struct ocfs2_super *osb)
229234
{
230235
struct ocfs2_recovery_map *rm;
@@ -1489,6 +1494,18 @@ static int __ocfs2_recovery_thread(void *arg)
14891494
}
14901495
}
14911496
restart:
1497+
if (quota_enabled) {
1498+
mutex_lock(&osb->recovery_lock);
1499+
/* Confirm that recovery thread will no longer recover quotas */
1500+
if (osb->recovery_state == OCFS2_REC_QUOTA_WANT_DISABLE) {
1501+
osb->recovery_state = OCFS2_REC_QUOTA_DISABLED;
1502+
wake_up(&osb->recovery_event);
1503+
}
1504+
if (osb->recovery_state >= OCFS2_REC_QUOTA_DISABLED)
1505+
quota_enabled = 0;
1506+
mutex_unlock(&osb->recovery_lock);
1507+
}
1508+
14921509
status = ocfs2_super_lock(osb, 1);
14931510
if (status < 0) {
14941511
mlog_errno(status);
@@ -1592,8 +1609,7 @@ static int __ocfs2_recovery_thread(void *arg)
15921609

15931610
mutex_unlock(&osb->recovery_lock);
15941611

1595-
if (quota_enabled)
1596-
kfree(rm_quota);
1612+
kfree(rm_quota);
15971613

15981614
return status;
15991615
}

fs/ocfs2/journal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ void ocfs2_wait_for_recovery(struct ocfs2_super *osb);
148148

149149
int ocfs2_recovery_init(struct ocfs2_super *osb);
150150
void ocfs2_recovery_exit(struct ocfs2_super *osb);
151+
void ocfs2_recovery_disable_quota(struct ocfs2_super *osb);
151152

152153
int ocfs2_compute_replay_slots(struct ocfs2_super *osb);
153154
void ocfs2_free_replay_slots(struct ocfs2_super *osb);

fs/ocfs2/ocfs2.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,12 @@ void ocfs2_initialize_journal_triggers(struct super_block *sb,
310310

311311
enum ocfs2_recovery_state {
312312
OCFS2_REC_ENABLED = 0,
313+
OCFS2_REC_QUOTA_WANT_DISABLE,
314+
/*
315+
* Must be OCFS2_REC_QUOTA_WANT_DISABLE + 1 for
316+
* ocfs2_recovery_disable_quota() to work.
317+
*/
318+
OCFS2_REC_QUOTA_DISABLED,
313319
OCFS2_REC_WANT_DISABLE,
314320
/*
315321
* Must be OCFS2_REC_WANT_DISABLE + 1 for ocfs2_recovery_exit() to work

fs/ocfs2/quota_local.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -453,8 +453,7 @@ struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
453453

454454
/* Sync changes in local quota file into global quota file and
455455
* reinitialize local quota file.
456-
* The function expects local quota file to be already locked and
457-
* s_umount locked in shared mode. */
456+
* The function expects local quota file to be already locked. */
458457
static int ocfs2_recover_local_quota_file(struct inode *lqinode,
459458
int type,
460459
struct ocfs2_quota_recovery *rec)
@@ -588,7 +587,6 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
588587
{
589588
unsigned int ino[OCFS2_MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
590589
LOCAL_GROUP_QUOTA_SYSTEM_INODE };
591-
struct super_block *sb = osb->sb;
592590
struct ocfs2_local_disk_dqinfo *ldinfo;
593591
struct buffer_head *bh;
594592
handle_t *handle;
@@ -600,7 +598,6 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
600598
printk(KERN_NOTICE "ocfs2: Finishing quota recovery on device (%s) for "
601599
"slot %u\n", osb->dev_str, slot_num);
602600

603-
down_read(&sb->s_umount);
604601
for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
605602
if (list_empty(&(rec->r_list[type])))
606603
continue;
@@ -677,7 +674,6 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
677674
break;
678675
}
679676
out:
680-
up_read(&sb->s_umount);
681677
kfree(rec);
682678
return status;
683679
}
@@ -843,8 +839,7 @@ static int ocfs2_local_free_info(struct super_block *sb, int type)
843839
ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
844840

845841
/*
846-
* s_umount held in exclusive mode protects us against racing with
847-
* recovery thread...
842+
* ocfs2_dismount_volume() has already aborted quota recovery...
848843
*/
849844
if (oinfo->dqi_rec) {
850845
ocfs2_free_quota_recovery(oinfo->dqi_rec);

fs/ocfs2/super.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,9 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
18121812
/* Orphan scan should be stopped as early as possible */
18131813
ocfs2_orphan_scan_stop(osb);
18141814

1815+
/* Stop quota recovery so that we can disable quotas */
1816+
ocfs2_recovery_disable_quota(osb);
1817+
18151818
ocfs2_disable_quotas(osb);
18161819

18171820
/* All dquots should be freed by now */

0 commit comments

Comments
 (0)