xen/x86: Workaround 'x86/ioapic: Add register level checks to detect bogus io-apic...
[pandora-kernel.git] / fs / dcache.c
index 89509b5..616fedf 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/prefetch.h>
 #include <linux/ratelimit.h>
 #include "internal.h"
+#include "mount.h"
 
 /*
  * Usage:
@@ -242,6 +243,7 @@ static void dentry_lru_add(struct dentry *dentry)
 static void __dentry_lru_del(struct dentry *dentry)
 {
        list_del_init(&dentry->d_lru);
+       dentry->d_flags &= ~DCACHE_SHRINK_LIST;
        dentry->d_sb->s_nr_dentry_unused--;
        dentry_stat.nr_unused--;
 }
@@ -275,15 +277,15 @@ static void dentry_lru_prune(struct dentry *dentry)
        }
 }
 
-static void dentry_lru_move_tail(struct dentry *dentry)
+static void dentry_lru_move_list(struct dentry *dentry, struct list_head *list)
 {
        spin_lock(&dcache_lru_lock);
        if (list_empty(&dentry->d_lru)) {
-               list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
+               list_add_tail(&dentry->d_lru, list);
                dentry->d_sb->s_nr_dentry_unused++;
                dentry_stat.nr_unused++;
        } else {
-               list_move_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
+               list_move_tail(&dentry->d_lru, list);
        }
        spin_unlock(&dcache_lru_lock);
 }
@@ -769,14 +771,18 @@ static void shrink_dentry_list(struct list_head *list)
 }
 
 /**
- * __shrink_dcache_sb - shrink the dentry LRU on a given superblock
- * @sb:                superblock to shrink dentry LRU.
- * @count:     number of entries to prune
- * @flags:     flags to control the dentry processing
+ * prune_dcache_sb - shrink the dcache
+ * @sb: superblock
+ * @count: number of entries to try to free
+ *
+ * Attempt to shrink the superblock dcache LRU by @count entries. This is
+ * done when we need more memory an called from the superblock shrinker
+ * function.
  *
- * If flags contains DCACHE_REFERENCED reference dentries will not be pruned.
+ * This function may fail to free any resources if all the dentries are in
+ * use.
  */
-static void __shrink_dcache_sb(struct super_block *sb, int count, int flags)
+void prune_dcache_sb(struct super_block *sb, int count)
 {
        struct dentry *dentry;
        LIST_HEAD(referenced);
@@ -795,18 +801,13 @@ relock:
                        goto relock;
                }
 
-               /*
-                * If we are honouring the DCACHE_REFERENCED flag and the
-                * dentry has this flag set, don't free it.  Clear the flag
-                * and put it back on the LRU.
-                */
-               if (flags & DCACHE_REFERENCED &&
-                               dentry->d_flags & DCACHE_REFERENCED) {
+               if (dentry->d_flags & DCACHE_REFERENCED) {
                        dentry->d_flags &= ~DCACHE_REFERENCED;
                        list_move(&dentry->d_lru, &referenced);
                        spin_unlock(&dentry->d_lock);
                } else {
                        list_move_tail(&dentry->d_lru, &tmp);
+                       dentry->d_flags |= DCACHE_SHRINK_LIST;
                        spin_unlock(&dentry->d_lock);
                        if (!--count)
                                break;
@@ -820,23 +821,6 @@ relock:
        shrink_dentry_list(&tmp);
 }
 
-/**
- * prune_dcache_sb - shrink the dcache
- * @sb: superblock
- * @nr_to_scan: number of entries to try to free
- *
- * Attempt to shrink the superblock dcache LRU by @nr_to_scan entries. This is
- * done when we need more memory an called from the superblock shrinker
- * function.
- *
- * This function may fail to free any resources if all the dentries are in
- * use.
- */
-void prune_dcache_sb(struct super_block *sb, int nr_to_scan)
-{
-       __shrink_dcache_sb(sb, nr_to_scan, DCACHE_REFERENCED);
-}
-
 /**
  * shrink_dcache_sb - shrink dcache for a superblock
  * @sb: superblock
@@ -1091,7 +1075,7 @@ EXPORT_SYMBOL(have_submounts);
  * drop the lock and return early due to latency
  * constraints.
  */
-static int select_parent(struct dentry * parent)
+static int select_parent(struct dentry *parent, struct list_head *dispose)
 {
        struct dentry *this_parent;
        struct list_head *next;
@@ -1113,17 +1097,21 @@ resume:
 
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
 
-               /* 
-                * move only zero ref count dentries to the end 
-                * of the unused list for prune_dcache
+               /*
+                * move only zero ref count dentries to the dispose list.
+                *
+                * Those which are presently on the shrink list, being processed
+                * by shrink_dentry_list(), shouldn't be moved.  Otherwise the
+                * loop in shrink_dcache_parent() might not make any progress
+                * and loop forever.
                 */
-               if (!dentry->d_count) {
-                       dentry_lru_move_tail(dentry);
-                       found++;
-               } else {
+               if (dentry->d_count) {
                        dentry_lru_del(dentry);
+               } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
+                       dentry_lru_move_list(dentry, dispose);
+                       dentry->d_flags |= DCACHE_SHRINK_LIST;
+                       found++;
                }
-
                /*
                 * We can return to the caller if we have found some (this
                 * ensures forward progress). We'll be coming back to find
@@ -1180,14 +1168,13 @@ rename_retry:
  *
  * Prune the dcache to remove unused children of the parent dentry.
  */
 void shrink_dcache_parent(struct dentry * parent)
 {
-       struct super_block *sb = parent->d_sb;
+       LIST_HEAD(dispose);
        int found;
 
-       while ((found = select_parent(parent)) != 0)
-               __shrink_dcache_sb(sb, found, 0);
+       while ((found = select_parent(parent, &dispose)) != 0)
+               shrink_dentry_list(&dispose);
 }
 EXPORT_SYMBOL(shrink_dcache_parent);
 
@@ -1460,6 +1447,23 @@ struct dentry * d_alloc_root(struct inode * root_inode)
 }
 EXPORT_SYMBOL(d_alloc_root);
 
+struct dentry *d_make_root(struct inode *root_inode)
+{
+       struct dentry *res = NULL;
+
+       if (root_inode) {
+               static const struct qstr name = { .name = "/", .len = 1 };
+
+               res = __d_alloc(root_inode->i_sb, &name);
+               if (res)
+                       d_instantiate(res, root_inode);
+               else
+                       iput(root_inode);
+       }
+       return res;
+}
+EXPORT_SYMBOL(d_make_root);
+
 static struct dentry * __d_find_any_alias(struct inode *inode)
 {
        struct dentry *alias;
@@ -2451,6 +2455,7 @@ static int prepend_path(const struct path *path,
 {
        struct dentry *dentry = path->dentry;
        struct vfsmount *vfsmnt = path->mnt;
+       struct mount *mnt = real_mount(vfsmnt);
        bool slash = false;
        int error = 0;
 
@@ -2460,11 +2465,11 @@ static int prepend_path(const struct path *path,
 
                if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
                        /* Global root? */
-                       if (vfsmnt->mnt_parent == vfsmnt) {
+                       if (!mnt_has_parent(mnt))
                                goto global_root;
-                       }
-                       dentry = vfsmnt->mnt_mountpoint;
-                       vfsmnt = vfsmnt->mnt_parent;
+                       dentry = mnt->mnt_mountpoint;
+                       mnt = mnt->mnt_parent;
+                       vfsmnt = &mnt->mnt;
                        continue;
                }
                parent = dentry->d_parent;
@@ -2501,7 +2506,7 @@ global_root:
        if (!slash)
                error = prepend(buffer, buflen, "/", 1);
        if (!error)
-               error = vfsmnt->mnt_ns ? 1 : 2;
+               error = real_mount(vfsmnt)->mnt_ns ? 1 : 2;
        goto out;
 }
 
@@ -2853,31 +2858,6 @@ int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
        return result;
 }
 
-int path_is_under(struct path *path1, struct path *path2)
-{
-       struct vfsmount *mnt = path1->mnt;
-       struct dentry *dentry = path1->dentry;
-       int res;
-
-       br_read_lock(vfsmount_lock);
-       if (mnt != path2->mnt) {
-               for (;;) {
-                       if (mnt->mnt_parent == mnt) {
-                               br_read_unlock(vfsmount_lock);
-                               return 0;
-                       }
-                       if (mnt->mnt_parent == path2->mnt)
-                               break;
-                       mnt = mnt->mnt_parent;
-               }
-               dentry = mnt->mnt_mountpoint;
-       }
-       res = is_subdir(dentry, path2->dentry);
-       br_read_unlock(vfsmount_lock);
-       return res;
-}
-EXPORT_SYMBOL(path_is_under);
-
 void d_genocide(struct dentry *root)
 {
        struct dentry *this_parent;