]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Quota deadlock fix
authorAndrew Morton <akpm@osdl.org>
Wed, 22 Oct 2003 01:20:48 +0000 (18:20 -0700)
committerLinus Torvalds <torvalds@home.osdl.org>
Wed, 22 Oct 2003 01:20:48 +0000 (18:20 -0700)
From: Jan Kara <jack@ucw.cz>

attached patch should fix a quota locking problem causing deadlock (when
inode was being released from icache and it caused newly created quota
structure to be written).

fs/dquot.c

index 9b3a6aae6c5f0cb4634c0e21813bc10fb213f381..00fc672dd334da6b22f4987014e6fab59899989e 100644 (file)
@@ -826,28 +826,49 @@ void dquot_initialize(struct inode *inode, int type)
 }
 
 /*
- * Release all quota for the specified inode.
- *
- * Note: this is a blocking operation.
+ *     Remove references to quota from inode
+ *     This function needs dqptr_sem for writing
  */
-static void dquot_drop_nolock(struct inode *inode)
+static void dquot_drop_iupdate(struct inode *inode, struct dquot **to_drop)
 {
        int cnt;
 
        inode->i_flags &= ~S_QUOTA;
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               if (inode->i_dquot[cnt] == NODQUOT)
-                       continue;
-               dqput(inode->i_dquot[cnt]);
+               to_drop[cnt] = inode->i_dquot[cnt];
                inode->i_dquot[cnt] = NODQUOT;
        }
 }
 
+/*
+ *     Release all quotas referenced by inode
+ */
 void dquot_drop(struct inode *inode)
 {
+       struct dquot *to_drop[MAXQUOTAS];
+       int cnt;
+
        down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       dquot_drop_nolock(inode);
+       dquot_drop_iupdate(inode, to_drop);
        up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+               if (to_drop[cnt] != NODQUOT)
+                       dqput(to_drop[cnt]);
+}
+
+/*
+ *     Release all quotas referenced by inode.
+ *     This function assumes dqptr_sem for writing
+ */
+void dquot_drop_nolock(struct inode *inode)
+{
+       struct dquot *to_drop[MAXQUOTAS];
+       int cnt;
+
+       dquot_drop_iupdate(inode, to_drop);
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+               if (to_drop[cnt] != NODQUOT)
+                       dqput(to_drop[cnt]);
 }
 
 /*
@@ -862,6 +883,10 @@ int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
                warntype[cnt] = NOWARN;
 
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+       if (IS_NOQUOTA(inode)) {
+               up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+               return QUOTA_OK;
+       }
        spin_lock(&dq_data_lock);
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (inode->i_dquot[cnt] == NODQUOT)
@@ -894,6 +919,10 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number)
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
                warntype[cnt] = NOWARN;
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+       if (IS_NOQUOTA(inode)) {
+               up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+               return QUOTA_OK;
+       }
        spin_lock(&dq_data_lock);
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (inode->i_dquot[cnt] == NODQUOT)
@@ -923,6 +952,10 @@ void dquot_free_space(struct inode *inode, qsize_t number)
        unsigned int cnt;
 
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+       if (IS_NOQUOTA(inode)) {
+               up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+               return;
+       }
        spin_lock(&dq_data_lock);
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (inode->i_dquot[cnt] == NODQUOT)
@@ -942,6 +975,10 @@ void dquot_free_inode(const struct inode *inode, unsigned long number)
        unsigned int cnt;
 
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+       if (IS_NOQUOTA(inode)) {
+               up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+               return;
+       }
        spin_lock(&dq_data_lock);
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (inode->i_dquot[cnt] == NODQUOT)