lockref: add ability to mark lockrefs "dead"
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Sep 2013 22:49:18 +0000 (15:49 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Sep 2013 22:49:18 +0000 (15:49 -0700)
The only actual current lockref user (dcache) uses zero reference counts
even for perfectly live dentries, because it's a cache: there may not be
any users, but that doesn't mean that we want to throw away the dentry.

At the same time, the dentry cache does have a notion of a truly "dead"
dentry that we must not even increment the reference count of, because
we have pruned it and it is not valid.

Currently that distinction is not visible in the lockref itself, and the
dentry cache validation uses "lockref_get_or_lock()" to either get a new
reference to a dentry that already had existing references (and thus
cannot be dead), or get the dentry lock so that we can then verify the
dentry and increment the reference count under the lock if that
verification was successful.

That's all somewhat complicated.

This adds the concept of being "dead" to the lockref itself, by simply
using a count that is negative.  This allows a usage scenario where we
can increment the refcount of a dentry without having to validate it,
and pushing the special "we killed it" case into the lockref code.

The dentry code itself doesn't actually use this yet, and it's probably
too late in the merge window to do that code (the dentry_kill() code
with its "should I decrement the count" logic really is pretty complex
code), but let's introduce the concept at the lockref level now.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/lockref.h
lib/lockref.c

index ca07b50..f279ed9 100644 (file)
@@ -33,4 +33,7 @@ extern int lockref_get_not_zero(struct lockref *);
 extern int lockref_get_or_lock(struct lockref *);
 extern int lockref_put_or_lock(struct lockref *);
 
+extern void lockref_mark_dead(struct lockref *);
+extern int lockref_get_not_dead(struct lockref *);
+
 #endif /* __LINUX_LOCKREF_H */
index 7aae8df..e2cd2c0 100644 (file)
@@ -126,3 +126,41 @@ int lockref_put_or_lock(struct lockref *lockref)
        return 1;
 }
 EXPORT_SYMBOL(lockref_put_or_lock);
+
+/**
+ * lockref_mark_dead - mark lockref dead
+ * @lockref: pointer to lockref structure
+ */
+void lockref_mark_dead(struct lockref *lockref)
+{
+       assert_spin_locked(&lockref->lock);
+       lockref->count = -128;
+}
+
+/**
+ * lockref_get_not_dead - Increments count unless the ref is dead
+ * @lockref: pointer to lockref structure
+ * Return: 1 if count updated successfully or 0 if lockref was dead
+ */
+int lockref_get_not_dead(struct lockref *lockref)
+{
+       int retval;
+
+       CMPXCHG_LOOP(
+               new.count++;
+               if ((int)old.count < 0)
+                       return 0;
+       ,
+               return 1;
+       );
+
+       spin_lock(&lockref->lock);
+       retval = 0;
+       if ((int) lockref->count >= 0) {
+               lockref->count++;
+               retval = 1;
+       }
+       spin_unlock(&lockref->lock);
+       return retval;
+}
+EXPORT_SYMBOL(lockref_get_not_dead);