diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 17:49:39 +1100 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 17:50:23 +1100 |
commit | 58db63d086790eec2ed433f9d8c4962239809cf8 (patch) | |
tree | e8fa4238167f92bb0dbded4d2df1498d8c4e9404 /fs/dcache.c | |
parent | b5c84bf6f6fa3a7dfdcb556023a62953574b60ee (diff) |
fs: dcache avoid starvation in dcache multi-step operations
Long lived dcache "multi-step" operations which retry on rename seq can
be starved with a lot of rename activity. If they fail after the 1st pass,
take the rename_lock for writing to avoid further starvation.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 56 |
1 files changed, 42 insertions, 14 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 0dbae053b664..bf6294a20f0e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -973,10 +973,11 @@ int have_submounts(struct dentry *parent) struct dentry *this_parent; struct list_head *next; unsigned seq; + int locked = 0; -rename_retry: - this_parent = parent; seq = read_seqbegin(&rename_lock); +again: + this_parent = parent; if (d_mountpoint(parent)) goto positive; @@ -1021,7 +1022,7 @@ resume: /* might go back up the wrong parent if we have had a rename * or deletion */ if (this_parent != child->d_parent || - read_seqretry(&rename_lock, seq)) { + (!locked && read_seqretry(&rename_lock, seq))) { spin_unlock(&this_parent->d_lock); rcu_read_unlock(); goto rename_retry; @@ -1031,13 +1032,22 @@ resume: goto resume; } spin_unlock(&this_parent->d_lock); - if (read_seqretry(&rename_lock, seq)) + if (!locked && read_seqretry(&rename_lock, seq)) goto rename_retry; + if (locked) + write_sequnlock(&rename_lock); return 0; /* No mount points found in tree */ positive: - if (read_seqretry(&rename_lock, seq)) + if (!locked && read_seqretry(&rename_lock, seq)) goto rename_retry; + if (locked) + write_sequnlock(&rename_lock); return 1; + +rename_retry: + locked = 1; + write_seqlock(&rename_lock); + goto again; } EXPORT_SYMBOL(have_submounts); @@ -1061,11 +1071,11 @@ static int select_parent(struct dentry * parent) struct list_head *next; unsigned seq; int found = 0; + int locked = 0; -rename_retry: - this_parent = parent; seq = read_seqbegin(&rename_lock); - +again: + this_parent = parent; spin_lock(&this_parent->d_lock); repeat: next = this_parent->d_subdirs.next; @@ -1127,7 +1137,7 @@ resume: /* might go back up the wrong parent if we have had a rename * or deletion */ if (this_parent != child->d_parent || - read_seqretry(&rename_lock, seq)) { + (!locked && read_seqretry(&rename_lock, seq))) { spin_unlock(&this_parent->d_lock); rcu_read_unlock(); goto rename_retry; @@ -1138,9 +1148,18 @@ resume: } out: spin_unlock(&this_parent->d_lock); - if (read_seqretry(&rename_lock, seq)) + if (!locked && read_seqretry(&rename_lock, seq)) goto rename_retry; + if (locked) + write_sequnlock(&rename_lock); return found; + +rename_retry: + if (found) + return found; + locked = 1; + write_seqlock(&rename_lock); + goto again; } /** @@ -2655,10 +2674,11 @@ void d_genocide(struct dentry *root) struct dentry *this_parent; struct list_head *next; unsigned seq; + int locked = 0; -rename_retry: - this_parent = root; seq = read_seqbegin(&rename_lock); +again: + this_parent = root; spin_lock(&this_parent->d_lock); repeat: next = this_parent->d_subdirs.next; @@ -2703,7 +2723,7 @@ resume: /* might go back up the wrong parent if we have had a rename * or deletion */ if (this_parent != child->d_parent || - read_seqretry(&rename_lock, seq)) { + (!locked && read_seqretry(&rename_lock, seq))) { spin_unlock(&this_parent->d_lock); rcu_read_unlock(); goto rename_retry; @@ -2713,8 +2733,16 @@ resume: goto resume; } spin_unlock(&this_parent->d_lock); - if (read_seqretry(&rename_lock, seq)) + if (!locked && read_seqretry(&rename_lock, seq)) goto rename_retry; + if (locked) + write_sequnlock(&rename_lock); + return; + +rename_retry: + locked = 1; + write_seqlock(&rename_lock); + goto again; } /** |