diff options
| author | Jan Kara <jack@suse.cz> | 2012-02-10 05:03:01 -0500 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-02-13 20:45:39 -0500 |
| commit | dcdbed853d9fbb0547b781ba676049b87f54129a (patch) | |
| tree | 6f8f2382e7aef99ec21ae9c4dfbfe18e47d4626c | |
| parent | 6b6dc836a195e077e76977b6c020a73de411b46d (diff) | |
quota: Fix deadlock with suspend and quotas
This script causes a kernel deadlock:
set -e
DEVICE=/dev/vg1/linear
lvchange -ay $DEVICE
mkfs.ext3 $DEVICE
mount -t ext3 -o usrquota,grpquota $DEVICE /mnt/test
quotacheck -gu /mnt/test
umount /mnt/test
mount -t ext3 -o usrquota,grpquota $DEVICE /mnt/test
quotaon /mnt/test
dmsetup suspend $DEVICE
setquota -u root 1 2 3 4 /mnt/test &
sleep 1
dmsetup resume $DEVICE
setquota acquired semaphore s_umount for read and then tried to perform a
transaction (and waits because the device is suspended). dmsetup resume tries
to acquire s_umount for write before resuming the device (and waits for
setquota).
Fix the deadlock by grabbing a thawed superblock for quota commands which need
it.
Reported-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
| -rw-r--r-- | fs/quota/quota.c | 24 |
1 files changed, 21 insertions, 3 deletions
diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 7898cd688a00..fc2c4388d126 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c | |||
| @@ -292,11 +292,26 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, | |||
| 292 | } | 292 | } |
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | /* Return 1 if 'cmd' will block on frozen filesystem */ | ||
| 296 | static int quotactl_cmd_write(int cmd) | ||
| 297 | { | ||
| 298 | switch (cmd) { | ||
| 299 | case Q_GETFMT: | ||
| 300 | case Q_GETINFO: | ||
| 301 | case Q_SYNC: | ||
| 302 | case Q_XGETQSTAT: | ||
| 303 | case Q_XGETQUOTA: | ||
| 304 | case Q_XQUOTASYNC: | ||
| 305 | return 0; | ||
| 306 | } | ||
| 307 | return 1; | ||
| 308 | } | ||
| 309 | |||
| 295 | /* | 310 | /* |
| 296 | * look up a superblock on which quota ops will be performed | 311 | * look up a superblock on which quota ops will be performed |
| 297 | * - use the name of a block device to find the superblock thereon | 312 | * - use the name of a block device to find the superblock thereon |
| 298 | */ | 313 | */ |
| 299 | static struct super_block *quotactl_block(const char __user *special) | 314 | static struct super_block *quotactl_block(const char __user *special, int cmd) |
| 300 | { | 315 | { |
| 301 | #ifdef CONFIG_BLOCK | 316 | #ifdef CONFIG_BLOCK |
| 302 | struct block_device *bdev; | 317 | struct block_device *bdev; |
| @@ -309,7 +324,10 @@ static struct super_block *quotactl_block(const char __user *special) | |||
| 309 | putname(tmp); | 324 | putname(tmp); |
| 310 | if (IS_ERR(bdev)) | 325 | if (IS_ERR(bdev)) |
| 311 | return ERR_CAST(bdev); | 326 | return ERR_CAST(bdev); |
| 312 | sb = get_super(bdev); | 327 | if (quotactl_cmd_write(cmd)) |
| 328 | sb = get_super_thawed(bdev); | ||
| 329 | else | ||
| 330 | sb = get_super(bdev); | ||
| 313 | bdput(bdev); | 331 | bdput(bdev); |
| 314 | if (!sb) | 332 | if (!sb) |
| 315 | return ERR_PTR(-ENODEV); | 333 | return ERR_PTR(-ENODEV); |
| @@ -361,7 +379,7 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, | |||
| 361 | pathp = &path; | 379 | pathp = &path; |
| 362 | } | 380 | } |
| 363 | 381 | ||
| 364 | sb = quotactl_block(special); | 382 | sb = quotactl_block(special, cmds); |
| 365 | if (IS_ERR(sb)) { | 383 | if (IS_ERR(sb)) { |
| 366 | ret = PTR_ERR(sb); | 384 | ret = PTR_ERR(sb); |
| 367 | goto out; | 385 | goto out; |
