fs: dcache avoid starvation in dcache multi-step operations
authorNick Piggin <npiggin@kernel.dk>
Fri, 7 Jan 2011 06:49:39 +0000 (17:49 +1100)
committerNick Piggin <npiggin@kernel.dk>
Fri, 7 Jan 2011 06:50:23 +0000 (17:50 +1100)
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>
fs/dcache.c

index 0dbae053b6649a1401d0606a665d9505384ea6a6..bf6294a20f0ebff37dfc0308e221c1df14029420 100644 (file)
@@ -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;
 }
 
 /**