Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-mmc
[pandora-kernel.git] / fs / dcache.c
index 19458d3..59dbc92 100644 (file)
@@ -359,12 +359,13 @@ restart:
 }
 
 /*
- * Throw away a dentry - free the inode, dput the parent.
- * This requires that the LRU list has already been
- * removed.
+ * Throw away a dentry - free the inode, dput the parent.  This requires that
+ * the LRU list has already been removed.
+ *
  * Called with dcache_lock, drops it and then regains.
+ * Called with dentry->d_lock held, drops it.
  */
-static inline void prune_one_dentry(struct dentry * dentry)
+static void prune_one_dentry(struct dentry * dentry)
 {
        struct dentry * parent;
 
@@ -382,6 +383,8 @@ static inline void prune_one_dentry(struct dentry * dentry)
 /**
  * prune_dcache - shrink the dcache
  * @count: number of entries to try and free
+ * @sb: if given, ignore dentries for other superblocks
+ *         which are being unmounted.
  *
  * Shrink the dcache. This is done when we need
  * more memory, or simply when we need to unmount
@@ -392,16 +395,29 @@ static inline void prune_one_dentry(struct dentry * dentry)
  * all the dentries are in use.
  */
  
-static void prune_dcache(int count)
+static void prune_dcache(int count, struct super_block *sb)
 {
        spin_lock(&dcache_lock);
        for (; count ; count--) {
                struct dentry *dentry;
                struct list_head *tmp;
+               struct rw_semaphore *s_umount;
 
                cond_resched_lock(&dcache_lock);
 
                tmp = dentry_unused.prev;
+               if (unlikely(sb)) {
+                       /* Try to find a dentry for this sb, but don't try
+                        * too hard, if they aren't near the tail they will
+                        * be moved down again soon
+                        */
+                       int skip = count;
+                       while (skip && tmp != &dentry_unused &&
+                           list_entry(tmp, struct dentry, d_lru)->d_sb != sb) {
+                               skip--;
+                               tmp = tmp->prev;
+                       }
+               }
                if (tmp == &dentry_unused)
                        break;
                list_del_init(tmp);
@@ -427,7 +443,45 @@ static void prune_dcache(int count)
                        spin_unlock(&dentry->d_lock);
                        continue;
                }
-               prune_one_dentry(dentry);
+               /*
+                * If the dentry is not DCACHED_REFERENCED, it is time
+                * to remove it from the dcache, provided the super block is
+                * NULL (which means we are trying to reclaim memory)
+                * or this dentry belongs to the same super block that
+                * we want to shrink.
+                */
+               /*
+                * If this dentry is for "my" filesystem, then I can prune it
+                * without taking the s_umount lock (I already hold it).
+                */
+               if (sb && dentry->d_sb == sb) {
+                       prune_one_dentry(dentry);
+                       continue;
+               }
+               /*
+                * ...otherwise we need to be sure this filesystem isn't being
+                * unmounted, otherwise we could race with
+                * generic_shutdown_super(), and end up holding a reference to
+                * an inode while the filesystem is unmounted.
+                * So we try to get s_umount, and make sure s_root isn't NULL.
+                * (Take a local copy of s_umount to avoid a use-after-free of
+                * `dentry').
+                */
+               s_umount = &dentry->d_sb->s_umount;
+               if (down_read_trylock(s_umount)) {
+                       if (dentry->d_sb->s_root != NULL) {
+                               prune_one_dentry(dentry);
+                               up_read(s_umount);
+                               continue;
+                       }
+                       up_read(s_umount);
+               }
+               spin_unlock(&dentry->d_lock);
+               /* Cannot remove the first dentry, and it isn't appropriate
+                * to move it to the head of the list, so give up, and try
+                * later
+                */
+               break;
        }
        spin_unlock(&dcache_lock);
 }
@@ -630,7 +684,7 @@ void shrink_dcache_parent(struct dentry * parent)
        int found;
 
        while ((found = select_parent(parent)) != 0)
-               prune_dcache(found);
+               prune_dcache(found, parent->d_sb);
 }
 
 /**
@@ -643,9 +697,10 @@ void shrink_dcache_parent(struct dentry * parent)
  * done under dcache_lock.
  *
  */
-void shrink_dcache_anon(struct hlist_head *head)
+void shrink_dcache_anon(struct super_block *sb)
 {
        struct hlist_node *lp;
+       struct hlist_head *head = &sb->s_anon;
        int found;
        do {
                found = 0;
@@ -668,7 +723,7 @@ void shrink_dcache_anon(struct hlist_head *head)
                        }
                }
                spin_unlock(&dcache_lock);
-               prune_dcache(found);
+               prune_dcache(found, sb);
        } while(found);
 }
 
@@ -689,7 +744,7 @@ static int shrink_dcache_memory(int nr, gfp_t gfp_mask)
        if (nr) {
                if (!(gfp_mask & __GFP_FS))
                        return -1;
-               prune_dcache(nr);
+               prune_dcache(nr, NULL);
        }
        return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
 }
@@ -1100,6 +1155,32 @@ next:
        return found;
 }
 
+/**
+ * d_hash_and_lookup - hash the qstr then search for a dentry
+ * @dir: Directory to search in
+ * @name: qstr of name we wish to find
+ *
+ * On hash failure or on lookup failure NULL is returned.
+ */
+struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name)
+{
+       struct dentry *dentry = NULL;
+
+       /*
+        * Check for a fs-specific hash function. Note that we must
+        * calculate the standard hash first, as the d_op->d_hash()
+        * routine may choose to leave the hash value unchanged.
+        */
+       name->hash = full_name_hash(name->name, name->len);
+       if (dir->d_op && dir->d_op->d_hash) {
+               if (dir->d_op->d_hash(dir, name) < 0)
+                       goto out;
+       }
+       dentry = d_lookup(dir, name);
+out:
+       return dentry;
+}
+
 /**
  * d_validate - verify dentry provided from insecure source
  * @dentry: The dentry alleged to be valid child of @dparent
@@ -1172,11 +1253,11 @@ void d_delete(struct dentry * dentry)
        spin_lock(&dentry->d_lock);
        isdir = S_ISDIR(dentry->d_inode->i_mode);
        if (atomic_read(&dentry->d_count) == 1) {
-               /* remove this and other inotify debug checks after 2.6.18 */
-               dentry->d_flags &= ~DCACHE_INOTIFY_PARENT_WATCHED;
-
                dentry_iput(dentry);
                fsnotify_nameremove(dentry, isdir);
+
+               /* remove this and other inotify debug checks after 2.6.18 */
+               dentry->d_flags &= ~DCACHE_INOTIFY_PARENT_WATCHED;
                return;
        }
 
@@ -1616,26 +1697,12 @@ ino_t find_inode_number(struct dentry *dir, struct qstr *name)
        struct dentry * dentry;
        ino_t ino = 0;
 
-       /*
-        * Check for a fs-specific hash function. Note that we must
-        * calculate the standard hash first, as the d_op->d_hash()
-        * routine may choose to leave the hash value unchanged.
-        */
-       name->hash = full_name_hash(name->name, name->len);
-       if (dir->d_op && dir->d_op->d_hash)
-       {
-               if (dir->d_op->d_hash(dir, name) != 0)
-                       goto out;
-       }
-
-       dentry = d_lookup(dir, name);
-       if (dentry)
-       {
+       dentry = d_hash_and_lookup(dir, name);
+       if (dentry) {
                if (dentry->d_inode)
                        ino = dentry->d_inode->i_ino;
                dput(dentry);
        }
-out:
        return ino;
 }