Skip to content

Commit

Permalink
quota: Hold s_umount in exclusive mode when enabling / disabling quotas
Browse files Browse the repository at this point in the history
Currently we hold s_umount semaphore only in shared mode when enabling
or disabling quotas and use dqonoff_mutex for serializing quota state
changes on a filesystem and also quota state changes with other places
depending on current quota state. Using dedicated mutex for this causes
possible deadlocks during filesystem freezing (see following commit for
details) so we transition to using s_umount semaphore for the necessary
synchronization whose lock ordering is properly handled by the
filesystem freezing code. As a start grab s_umount in exclusive mode
when enabling / disabling quotas.

Signed-off-by: Jan Kara <jack@suse.cz>
  • Loading branch information
Jan Kara committed Nov 24, 2016
1 parent ba6379f commit 7d6cd73
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 3 deletions.
11 changes: 11 additions & 0 deletions fs/quota/dquot.c
Original file line number Diff line number Diff line change
Expand Up @@ -2107,6 +2107,10 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
struct quota_info *dqopt = sb_dqopt(sb);
struct inode *toputinode[MAXQUOTAS];

/* s_umount should be held in exclusive mode */
if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount)))
up_read(&sb->s_umount);

/* Cannot turn off usage accounting without turning off limits, or
* suspend quotas and simultaneously turn quotas off. */
if ((flags & DQUOT_USAGE_ENABLED && !(flags & DQUOT_LIMITS_ENABLED))
Expand Down Expand Up @@ -2371,6 +2375,10 @@ int dquot_resume(struct super_block *sb, int type)
int ret = 0, cnt;
unsigned int flags;

/* s_umount should be held in exclusive mode */
if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount)))
up_read(&sb->s_umount);

for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type)
continue;
Expand Down Expand Up @@ -2430,6 +2438,9 @@ int dquot_enable(struct inode *inode, int type, int format_id,

/* Just unsuspend quotas? */
BUG_ON(flags & DQUOT_SUSPENDED);
/* s_umount should be held in exclusive mode */
if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount)))
up_read(&sb->s_umount);

if (!flags)
return 0;
Expand Down
16 changes: 13 additions & 3 deletions fs/quota/quota.c
Original file line number Diff line number Diff line change
Expand Up @@ -789,9 +789,14 @@ static int quotactl_cmd_write(int cmd)
}
return 1;
}

#endif /* CONFIG_BLOCK */

/* Return true if quotactl command is manipulating quota on/off state */
static bool quotactl_cmd_onoff(int cmd)
{
return (cmd == Q_QUOTAON) || (cmd == Q_QUOTAOFF);
}

/*
* look up a superblock on which quota ops will be performed
* - use the name of a block device to find the superblock thereon
Expand All @@ -809,7 +814,9 @@ static struct super_block *quotactl_block(const char __user *special, int cmd)
putname(tmp);
if (IS_ERR(bdev))
return ERR_CAST(bdev);
if (quotactl_cmd_write(cmd))
if (quotactl_cmd_onoff(cmd))
sb = get_super_exclusive_thawed(bdev);
else if (quotactl_cmd_write(cmd))
sb = get_super_thawed(bdev);
else
sb = get_super(bdev);
Expand Down Expand Up @@ -872,7 +879,10 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,

ret = do_quotactl(sb, type, cmds, id, addr, pathp);

drop_super(sb);
if (!quotactl_cmd_onoff(cmds))
drop_super(sb);
else
drop_super_exclusive(sb);
out:
if (pathp && !IS_ERR(pathp))
path_put(pathp);
Expand Down

0 comments on commit 7d6cd73

Please sign in to comment.