[PATCH] Fix deadlock in journalled quota

From: Jan Kara
Date: Wed Apr 28 2004 - 08:49:34 EST


Hello!

Attached patch should fix reported deadlock in journalled quota code.
quotactl() call was violating the locking rules and didn't start
transaction when should. Attached patch fixes that. Please apply.

Honza

diff -ruX /home/jack/.kerndiffexclude linux-2.6.5-5-dirtylist/fs/dquot.c linux-2.6.5-6-quotactlfix/fs/dquot.c
--- linux-2.6.5-5-dirtylist/fs/dquot.c 2004-04-21 17:03:12.000000000 +0200
+++ linux-2.6.5-6-quotactlfix/fs/dquot.c 2004-04-28 10:02:36.000000000 +0200
@@ -529,7 +529,7 @@
clear_dquot_dirty(dquot);
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
spin_unlock(&dq_list_lock);
- dquot_release(dquot);
+ dquot->dq_sb->dq_op->release_dquot(dquot);
goto we_slept;
}
atomic_dec(&dquot->dq_count);
@@ -605,7 +605,7 @@
* finished or it will be canceled due to dq_count > 1 test */
wait_on_dquot(dquot);
/* Read the dquot and instantiate it (everything done only if needed) */
- if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && dquot_acquire(dquot) < 0) {
+ if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && sb->dq_op->acquire_dquot(dquot) < 0) {
dqput(dquot);
return NODQUOT;
}
@@ -1259,6 +1259,8 @@
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
.write_dquot = dquot_commit,
+ .acquire_dquot = dquot_acquire,
+ .release_dquot = dquot_release,
.mark_dirty = dquot_mark_dquot_dirty,
.write_info = dquot_commit_info
};
diff -ruX /home/jack/.kerndiffexclude linux-2.6.5-5-dirtylist/fs/ext3/super.c linux-2.6.5-6-quotactlfix/fs/ext3/super.c
--- linux-2.6.5-5-dirtylist/fs/ext3/super.c 2004-04-21 17:25:03.000000000 +0200
+++ linux-2.6.5-6-quotactlfix/fs/ext3/super.c 2004-04-28 10:07:41.000000000 +0200
@@ -521,6 +521,8 @@
static int ext3_dquot_initialize(struct inode *inode, int type);
static int ext3_dquot_drop(struct inode *inode);
static int ext3_write_dquot(struct dquot *dquot);
+static int ext3_acquire_dquot(struct dquot *dquot);
+static int ext3_release_dquot(struct dquot *dquot);
static int ext3_mark_dquot_dirty(struct dquot *dquot);
static int ext3_write_info(struct super_block *sb, int type);
static int ext3_quota_on(struct super_block *sb, int type, int format_id, char *path);
@@ -536,6 +538,8 @@
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
.write_dquot = ext3_write_dquot,
+ .acquire_dquot = ext3_acquire_dquot,
+ .release_dquot = ext3_release_dquot,
.mark_dirty = ext3_mark_dquot_dirty,
.write_info = ext3_write_info
};
@@ -2174,6 +2178,38 @@
return ret;
}

+static int ext3_acquire_dquot(struct dquot *dquot)
+{
+ int ret, err;
+ handle_t *handle;
+
+ handle = ext3_journal_start(dquot_to_inode(dquot),
+ EXT3_QUOTA_INIT_BLOCKS);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ ret = dquot_acquire(dquot);
+ err = ext3_journal_stop(handle);
+ if (!ret)
+ ret = err;
+ return ret;
+}
+
+static int ext3_release_dquot(struct dquot *dquot)
+{
+ int ret, err;
+ handle_t *handle;
+
+ handle = ext3_journal_start(dquot_to_inode(dquot),
+ EXT3_QUOTA_INIT_BLOCKS);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ ret = dquot_release(dquot);
+ err = ext3_journal_stop(handle);
+ if (!ret)
+ ret = err;
+ return ret;
+}
+
static int ext3_mark_dquot_dirty(struct dquot * dquot)
{
/* Are we journalling quotas? */
diff -ruX /home/jack/.kerndiffexclude linux-2.6.5-5-dirtylist/include/linux/quota.h linux-2.6.5-6-quotactlfix/include/linux/quota.h
--- linux-2.6.5-5-dirtylist/include/linux/quota.h 2004-04-21 10:59:40.000000000 +0200
+++ linux-2.6.5-6-quotactlfix/include/linux/quota.h 2004-04-28 09:58:26.000000000 +0200
@@ -250,6 +250,8 @@
int (*free_inode) (const struct inode *, unsigned long);
int (*transfer) (struct inode *, struct iattr *);
int (*write_dquot) (struct dquot *); /* Ordinary dquot write */
+ int (*acquire_dquot) (struct dquot *); /* Quota is going to be created on disk */
+ int (*release_dquot) (struct dquot *); /* Quota is going to be deleted from disk */
int (*mark_dirty) (struct dquot *); /* Dquot is marked dirty */
int (*write_info) (struct super_block *, int); /* Write of quota "superblock" */
};
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/