]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/dcache.c
Merge branches 'hyperv', 'multitouch', 'roccat', 'upstream', 'upstream-fixes', 'wacom...
[karo-tx-linux.git] / fs / dcache.c
index a88948b8bd17d8b16c42af20ee55bcf47f358923..10ba92def3f675985871f0fc72e9c6d7126357a3 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/bit_spinlock.h>
 #include <linux/rculist_bl.h>
 #include <linux/prefetch.h>
+#include <linux/ratelimit.h>
 #include "internal.h"
 
 /*
@@ -225,7 +226,7 @@ static void dentry_unlink_inode(struct dentry * dentry)
 }
 
 /*
- * dentry_lru_(add|del|move_tail) must be called with d_lock held.
+ * dentry_lru_(add|del|prune|move_tail) must be called with d_lock held.
  */
 static void dentry_lru_add(struct dentry *dentry)
 {
@@ -245,6 +246,9 @@ static void __dentry_lru_del(struct dentry *dentry)
        dentry_stat.nr_unused--;
 }
 
+/*
+ * Remove a dentry with references from the LRU.
+ */
 static void dentry_lru_del(struct dentry *dentry)
 {
        if (!list_empty(&dentry->d_lru)) {
@@ -254,6 +258,23 @@ static void dentry_lru_del(struct dentry *dentry)
        }
 }
 
+/*
+ * Remove a dentry that is unreferenced and about to be pruned
+ * (unhashed and destroyed) from the LRU, and inform the file system.
+ * This wrapper should be called _prior_ to unhashing a victim dentry.
+ */
+static void dentry_lru_prune(struct dentry *dentry)
+{
+       if (!list_empty(&dentry->d_lru)) {
+               if (dentry->d_flags & DCACHE_OP_PRUNE)
+                       dentry->d_op->d_prune(dentry);
+
+               spin_lock(&dcache_lru_lock);
+               __dentry_lru_del(dentry);
+               spin_unlock(&dcache_lru_lock);
+       }
+}
+
 static void dentry_lru_move_tail(struct dentry *dentry)
 {
        spin_lock(&dcache_lru_lock);
@@ -403,8 +424,12 @@ relock:
 
        if (ref)
                dentry->d_count--;
-       /* if dentry was on the d_lru list delete it from there */
-       dentry_lru_del(dentry);
+       /*
+        * if dentry was on the d_lru list delete it from there.
+        * inform the fs via d_prune that this dentry is about to be
+        * unhashed and destroyed.
+        */
+       dentry_lru_prune(dentry);
        /* if it was on the hash then remove it */
        __d_drop(dentry);
        return d_kill(dentry, parent);
@@ -522,9 +547,11 @@ int d_invalidate(struct dentry * dentry)
         * would make it unreachable from the root,
         * we might still populate it if it was a
         * working directory or similar).
+        * We also need to leave mountpoints alone,
+        * directory or not.
         */
-       if (dentry->d_count > 1) {
-               if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+       if (dentry->d_count > 1 && dentry->d_inode) {
+               if (S_ISDIR(dentry->d_inode->i_mode) || d_mountpoint(dentry)) {
                        spin_unlock(&dentry->d_lock);
                        return -EBUSY;
                }
@@ -854,8 +881,12 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                do {
                        struct inode *inode;
 
-                       /* detach from the system */
-                       dentry_lru_del(dentry);
+                       /*
+                        * remove the dentry from the lru, and inform
+                        * the fs that this dentry is about to be
+                        * unhashed and destroyed.
+                        */
+                       dentry_lru_prune(dentry);
                        __d_shrink(dentry);
 
                        if (dentry->d_count != 0) {
@@ -1283,6 +1314,8 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
                dentry->d_flags |= DCACHE_OP_REVALIDATE;
        if (op->d_delete)
                dentry->d_flags |= DCACHE_OP_DELETE;
+       if (op->d_prune)
+               dentry->d_flags |= DCACHE_OP_PRUNE;
 
 }
 EXPORT_SYMBOL(d_set_d_op);
@@ -2351,8 +2384,16 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)
                                actual = __d_unalias(inode, dentry, alias);
                        }
                        write_sequnlock(&rename_lock);
-                       if (IS_ERR(actual))
+                       if (IS_ERR(actual)) {
+                               if (PTR_ERR(actual) == -ELOOP)
+                                       pr_warn_ratelimited(
+                                               "VFS: Lookup of '%s' in %s %s"
+                                               " would have caused loop\n",
+                                               dentry->d_name.name,
+                                               inode->i_sb->s_type->name,
+                                               inode->i_sb->s_id);
                                dput(alias);
+                       }
                        goto out_nolock;
                }
        }