summaryrefslogtreecommitdiff
path: root/fs/quota/quota.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2016-11-23 13:16:10 +0100
committerJan Kara <jack@suse.cz>2016-11-24 15:26:53 +0100
commit7d6cd73d33b62021111a469b6a454ec357be295f (patch)
tree7feaea7fdb5d8d49c07a0836fd6f9dcc54290e69 /fs/quota/quota.c
parentba6379f7e6c7e51b3c0e92672bc61bb6961c2b5e (diff)
quota: Hold s_umount in exclusive mode when enabling / disabling quotas
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>
Diffstat (limited to 'fs/quota/quota.c')
-rw-r--r--fs/quota/quota.c16
1 files changed, 13 insertions, 3 deletions
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 2d445425aad7..6ce6f4b6826b 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -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
@@ -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);
@@ -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);