Commit 941d2380 authored by Jan Kara's avatar Jan Kara Committed by Linus Torvalds

quota: improve inode list scanning in add_dquot_ref()

We restarted scan of sb->s_inodes list whenever we had to drop inode_lock
in add_dquot_ref().  This leads to overall quadratic running time and thus
add_dquot_ref() can take several minutes when called on a life filesystem.
We fix the problem by using the fact that inode cannot be removed from
s_inodes list while we hold a reference to it and thus we can safely
restart the scan if we don't drop the reference.  Here we use the fact that
inodes freshly added to s_inodes list are already guaranteed to have quotas
properly initialized and the ordering of inodes on s_inodes list does not
change so we cannot skip any inode.

Thanks goes to Nick <gentuu@gmail.com> for analyzing the problem and
testing the fix.

[akpm@linux-foundation.org: iput(NULL) is legal]
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Cc: Nick <gentuu@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent bed9759b
...@@ -696,9 +696,8 @@ static int dqinit_needed(struct inode *inode, int type) ...@@ -696,9 +696,8 @@ static int dqinit_needed(struct inode *inode, int type)
/* This routine is guarded by dqonoff_mutex mutex */ /* This routine is guarded by dqonoff_mutex mutex */
static void add_dquot_ref(struct super_block *sb, int type) static void add_dquot_ref(struct super_block *sb, int type)
{ {
struct inode *inode; struct inode *inode, *old_inode = NULL;
restart:
spin_lock(&inode_lock); spin_lock(&inode_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
if (!atomic_read(&inode->i_writecount)) if (!atomic_read(&inode->i_writecount))
...@@ -711,12 +710,18 @@ static void add_dquot_ref(struct super_block *sb, int type) ...@@ -711,12 +710,18 @@ static void add_dquot_ref(struct super_block *sb, int type)
__iget(inode); __iget(inode);
spin_unlock(&inode_lock); spin_unlock(&inode_lock);
iput(old_inode);
sb->dq_op->initialize(inode, type); sb->dq_op->initialize(inode, type);
iput(inode); /* We hold a reference to 'inode' so it couldn't have been
/* As we may have blocked we had better restart... */ * removed from s_inodes list while we dropped the inode_lock.
goto restart; * We cannot iput the inode now as we can be holding the last
* reference and we cannot iput it under inode_lock. So we
* keep the reference and iput it later. */
old_inode = inode;
spin_lock(&inode_lock);
} }
spin_unlock(&inode_lock); spin_unlock(&inode_lock);
iput(old_inode);
} }
/* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */ /* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment