diff options
author | Jan Kara <jack@suse.cz> | 2008-02-06 04:37:36 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-06 13:41:07 -0500 |
commit | 941d2380e979dfefb6c824452e9f42be3ef948ee (patch) | |
tree | 4aad1ad817fb2043b8191ef77ec96845b9c24313 | |
parent | bed9759b2e6bd938097389f6bd2ac8d622fa3884 (diff) |
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: Jan Kara <jack@suse.cz>
Cc: Nick <gentuu@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/dquot.c | 15 |
1 files changed, 10 insertions, 5 deletions
diff --git a/fs/dquot.c b/fs/dquot.c index cee7c6f428f0..def4e969df77 100644 --- a/fs/dquot.c +++ b/fs/dquot.c | |||
@@ -696,9 +696,8 @@ static int dqinit_needed(struct inode *inode, int type) | |||
696 | /* This routine is guarded by dqonoff_mutex mutex */ | 696 | /* This routine is guarded by dqonoff_mutex mutex */ |
697 | static void add_dquot_ref(struct super_block *sb, int type) | 697 | static void add_dquot_ref(struct super_block *sb, int type) |
698 | { | 698 | { |
699 | struct inode *inode; | 699 | struct inode *inode, *old_inode = NULL; |
700 | 700 | ||
701 | restart: | ||
702 | spin_lock(&inode_lock); | 701 | spin_lock(&inode_lock); |
703 | list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { | 702 | list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { |
704 | if (!atomic_read(&inode->i_writecount)) | 703 | if (!atomic_read(&inode->i_writecount)) |
@@ -711,12 +710,18 @@ restart: | |||
711 | __iget(inode); | 710 | __iget(inode); |
712 | spin_unlock(&inode_lock); | 711 | spin_unlock(&inode_lock); |
713 | 712 | ||
713 | iput(old_inode); | ||
714 | sb->dq_op->initialize(inode, type); | 714 | sb->dq_op->initialize(inode, type); |
715 | iput(inode); | 715 | /* We hold a reference to 'inode' so it couldn't have been |
716 | /* As we may have blocked we had better restart... */ | 716 | * removed from s_inodes list while we dropped the inode_lock. |
717 | goto restart; | 717 | * We cannot iput the inode now as we can be holding the last |
718 | * reference and we cannot iput it under inode_lock. So we | ||
719 | * keep the reference and iput it later. */ | ||
720 | old_inode = inode; | ||
721 | spin_lock(&inode_lock); | ||
718 | } | 722 | } |
719 | spin_unlock(&inode_lock); | 723 | spin_unlock(&inode_lock); |
724 | iput(old_inode); | ||
720 | } | 725 | } |
721 | 726 | ||
722 | /* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */ | 727 | /* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */ |