IB: expand ib_umem_get() prototype
[safe/jmp/linux-2.6] / fs / dcache.c
index 681cab8..3ee588d 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/fsnotify.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-#include <linux/smp_lock.h>
 #include <linux/hash.h>
 #include <linux/cache.h>
 #include <linux/module.h>
@@ -39,7 +38,7 @@ int sysctl_vfs_cache_pressure __read_mostly = 100;
 EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure);
 
  __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lock);
-static __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock);
+__cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock);
 
 EXPORT_SYMBOL(dcache_lock);
 
@@ -90,12 +89,20 @@ static void d_free(struct dentry *dentry)
        if (dentry->d_op && dentry->d_op->d_release)
                dentry->d_op->d_release(dentry);
        /* if dentry was never inserted into hash, immediate free is OK */
-       if (dentry->d_hash.pprev == NULL)
+       if (hlist_unhashed(&dentry->d_hash))
                __d_free(dentry);
        else
                call_rcu(&dentry->d_u.d_rcu, d_callback);
 }
 
+static void dentry_lru_remove(struct dentry *dentry)
+{
+       if (!list_empty(&dentry->d_lru)) {
+               list_del_init(&dentry->d_lru);
+               dentry_stat.nr_unused--;
+       }
+}
+
 /*
  * Release the dentry's inode, using the filesystem
  * d_iput() operation if defined.
@@ -212,13 +219,7 @@ repeat:
 unhash_it:
        __d_drop(dentry);
 kill_it:
-       /* If dentry was on d_lru list
-        * delete it from there
-        */
-       if (!list_empty(&dentry->d_lru)) {
-               list_del(&dentry->d_lru);
-               dentry_stat.nr_unused--;
-       }
+       dentry_lru_remove(dentry);
        dentry = d_kill(dentry);
        if (dentry)
                goto repeat;
@@ -286,10 +287,7 @@ int d_invalidate(struct dentry * dentry)
 static inline struct dentry * __dget_locked(struct dentry *dentry)
 {
        atomic_inc(&dentry->d_count);
-       if (!list_empty(&dentry->d_lru)) {
-               dentry_stat.nr_unused--;
-               list_del_init(&dentry->d_lru);
-       }
+       dentry_lru_remove(dentry);
        return dentry;
 }
 
@@ -382,20 +380,17 @@ restart:
  * Throw away a dentry - free the inode, dput the parent.  This requires that
  * the LRU list has already been removed.
  *
- * If prune_parents is true, try to prune ancestors as well.
+ * Try to prune ancestors as well.  This is necessary to prevent
+ * quadratic behavior of shrink_dcache_parent(), but is also expected
+ * to be beneficial in reducing dentry cache fragmentation.
  *
  * Called with dcache_lock, drops it and then regains.
  * Called with dentry->d_lock held, drops it.
  */
-static void prune_one_dentry(struct dentry * dentry, int prune_parents)
+static void prune_one_dentry(struct dentry * dentry)
 {
        __d_drop(dentry);
        dentry = d_kill(dentry);
-       if (!prune_parents) {
-               dput(dentry);
-               spin_lock(&dcache_lock);
-               return;
-       }
 
        /*
         * Prune ancestors.  Locking is simpler than in dput(),
@@ -408,10 +403,7 @@ static void prune_one_dentry(struct dentry * dentry, int prune_parents)
 
                if (dentry->d_op && dentry->d_op->d_delete)
                        dentry->d_op->d_delete(dentry);
-               if (!list_empty(&dentry->d_lru)) {
-                       list_del(&dentry->d_lru);
-                       dentry_stat.nr_unused--;
-               }
+               dentry_lru_remove(dentry);
                __d_drop(dentry);
                dentry = d_kill(dentry);
                spin_lock(&dcache_lock);
@@ -423,7 +415,6 @@ static void prune_one_dentry(struct dentry * dentry, int prune_parents)
  * @count: number of entries to try and free
  * @sb: if given, ignore dentries for other superblocks
  *         which are being unmounted.
- * @prune_parents: if true, try to prune ancestors as well in one go
  *
  * Shrink the dcache. This is done when we need
  * more memory, or simply when we need to unmount
@@ -434,7 +425,7 @@ static void prune_one_dentry(struct dentry * dentry, int prune_parents)
  * all the dentries are in use.
  */
  
-static void prune_dcache(int count, struct super_block *sb, int prune_parents)
+static void prune_dcache(int count, struct super_block *sb)
 {
        spin_lock(&dcache_lock);
        for (; count ; count--) {
@@ -494,7 +485,7 @@ static void prune_dcache(int count, struct super_block *sb, int prune_parents)
                 * without taking the s_umount lock (I already hold it).
                 */
                if (sb && dentry->d_sb == sb) {
-                       prune_one_dentry(dentry, prune_parents);
+                       prune_one_dentry(dentry);
                        continue;
                }
                /*
@@ -509,7 +500,7 @@ static void prune_dcache(int count, struct super_block *sb, int prune_parents)
                s_umount = &dentry->d_sb->s_umount;
                if (down_read_trylock(s_umount)) {
                        if (dentry->d_sb->s_root != NULL) {
-                               prune_one_dentry(dentry, prune_parents);
+                               prune_one_dentry(dentry);
                                up_read(s_umount);
                                continue;
                        }
@@ -558,18 +549,18 @@ void shrink_dcache_sb(struct super_block * sb)
         * superblock to the most recent end of the unused list.
         */
        spin_lock(&dcache_lock);
-       list_for_each_safe(tmp, next, &dentry_unused) {
+       list_for_each_prev_safe(tmp, next, &dentry_unused) {
                dentry = list_entry(tmp, struct dentry, d_lru);
                if (dentry->d_sb != sb)
                        continue;
-               list_move(tmp, &dentry_unused);
+               list_move_tail(tmp, &dentry_unused);
        }
 
        /*
         * Pass two ... free the dentries for this superblock.
         */
 repeat:
-       list_for_each_safe(tmp, next, &dentry_unused) {
+       list_for_each_prev_safe(tmp, next, &dentry_unused) {
                dentry = list_entry(tmp, struct dentry, d_lru);
                if (dentry->d_sb != sb)
                        continue;
@@ -580,7 +571,7 @@ repeat:
                        spin_unlock(&dentry->d_lock);
                        continue;
                }
-               prune_one_dentry(dentry, 1);
+               prune_one_dentry(dentry);
                cond_resched_lock(&dcache_lock);
                goto repeat;
        }
@@ -601,10 +592,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
 
        /* detach this root from the system */
        spin_lock(&dcache_lock);
-       if (!list_empty(&dentry->d_lru)) {
-               dentry_stat.nr_unused--;
-               list_del_init(&dentry->d_lru);
-       }
+       dentry_lru_remove(dentry);
        __d_drop(dentry);
        spin_unlock(&dcache_lock);
 
@@ -618,11 +606,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                        spin_lock(&dcache_lock);
                        list_for_each_entry(loop, &dentry->d_subdirs,
                                            d_u.d_child) {
-                               if (!list_empty(&loop->d_lru)) {
-                                       dentry_stat.nr_unused--;
-                                       list_del_init(&loop->d_lru);
-                               }
-
+                               dentry_lru_remove(loop);
                                __d_drop(loop);
                                cond_resched_lock(&dcache_lock);
                        }
@@ -804,10 +788,7 @@ resume:
                struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
                next = tmp->next;
 
-               if (!list_empty(&dentry->d_lru)) {
-                       dentry_stat.nr_unused--;
-                       list_del_init(&dentry->d_lru);
-               }
+               dentry_lru_remove(dentry);
                /* 
                 * move only zero ref count dentries to the end 
                 * of the unused list for prune_dcache
@@ -859,7 +840,7 @@ void shrink_dcache_parent(struct dentry * parent)
        int found;
 
        while ((found = select_parent(parent)) != 0)
-               prune_dcache(found, parent->d_sb, 1);
+               prune_dcache(found, parent->d_sb);
 }
 
 /*
@@ -879,11 +860,16 @@ static int shrink_dcache_memory(int nr, gfp_t gfp_mask)
        if (nr) {
                if (!(gfp_mask & __GFP_FS))
                        return -1;
-               prune_dcache(nr, NULL, 0);
+               prune_dcache(nr, NULL);
        }
        return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
 }
 
+static struct shrinker dcache_shrinker = {
+       .shrink = shrink_dcache_memory,
+       .seeks = DEFAULT_SEEKS,
+};
+
 /**
  * d_alloc     -       allocate a dcache entry
  * @parent: parent of entry to allocate
@@ -899,7 +885,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
        struct dentry *dentry;
        char *dname;
 
-       dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); 
+       dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
        if (!dentry)
                return NULL;
 
@@ -1408,9 +1394,6 @@ void d_delete(struct dentry * dentry)
        if (atomic_read(&dentry->d_count) == 1) {
                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;
        }
 
@@ -1479,6 +1462,8 @@ static void switch_names(struct dentry *dentry, struct dentry *target)
                         * dentry:internal, target:external.  Steal target's
                         * storage and make target internal.
                         */
+                       memcpy(target->d_iname, dentry->d_name.name,
+                                       dentry->d_name.len + 1);
                        dentry->d_name.name = target->d_name.name;
                        target->d_name.name = target->d_iname;
                }
@@ -1510,8 +1495,8 @@ static void switch_names(struct dentry *dentry, struct dentry *target)
  * This forceful removal will result in ugly /proc output if
  * somebody holds a file open that got deleted due to a rename.
  * We could be nicer about the deleted file, and let it show
- * up under the name it got deleted rather than the name that
- * deleted it.
+ * up under the name it had before it was deleted rather than
+ * under the original name of the file that was moved on top of it.
  */
  
 /*
@@ -1542,7 +1527,7 @@ static void d_move_locked(struct dentry * dentry, struct dentry * target)
        }
 
        /* Move the dentry to the target hash queue, if on different bucket */
-       if (dentry->d_flags & DCACHE_UNHASHED)
+       if (d_unhashed(dentry))
                goto already_unhashed;
 
        hlist_del_rcu(&dentry->d_hash);
@@ -1761,12 +1746,21 @@ shouldnt_be_hashed:
        goto shouldnt_be_hashed;
 }
 
+static int prepend(char **buffer, int *buflen, const char *str,
+                         int namelen)
+{
+       *buflen -= namelen;
+       if (*buflen < 0)
+               return -ENAMETOOLONG;
+       *buffer -= namelen;
+       memcpy(*buffer, str, namelen);
+       return 0;
+}
+
 /**
  * d_path - return the path of a dentry
- * @dentry: dentry to report
- * @vfsmnt: vfsmnt to which the dentry belongs
- * @root: root dentry
- * @rootmnt: vfsmnt to which the root dentry belongs
+ * @path: the dentry/vfsmount to report
+ * @root: root vfsmnt/dentry (may be modified by this function)
  * @buffer: buffer to return value in
  * @buflen: buffer length
  *
@@ -1776,24 +1770,22 @@ shouldnt_be_hashed:
  * Returns the buffer or an error code if the path was too long.
  *
  * "buflen" should be positive. Caller holds the dcache_lock.
+ *
+ * If path is not reachable from the supplied root, then the value of
+ * root is changed (without modifying refcounts).
  */
-static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
-                       struct dentry *root, struct vfsmount *rootmnt,
-                       char *buffer, int buflen)
+char *__d_path(const struct path *path, struct path *root,
+              char *buffer, int buflen)
 {
+       struct dentry *dentry = path->dentry;
+       struct vfsmount *vfsmnt = path->mnt;
        char * end = buffer+buflen;
        char * retval;
-       int namelen;
-
-       *--end = '\0';
-       buflen--;
-       if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
-               buflen -= 10;
-               end -= 10;
-               if (buflen < 0)
+
+       prepend(&end, &buflen, "\0", 1);
+       if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
+               (prepend(&end, &buflen, " (deleted)", 10) != 0))
                        goto Elong;
-               memcpy(end, " (deleted)", 10);
-       }
 
        if (buflen < 1)
                goto Elong;
@@ -1804,7 +1796,7 @@ static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
        for (;;) {
                struct dentry * parent;
 
-               if (dentry == root && vfsmnt == rootmnt)
+               if (dentry == root->dentry && vfsmnt == root->mnt)
                        break;
                if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
                        /* Global root? */
@@ -1820,13 +1812,10 @@ static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
                }
                parent = dentry->d_parent;
                prefetch(parent);
-               namelen = dentry->d_name.len;
-               buflen -= namelen + 1;
-               if (buflen < 0)
+               if ((prepend(&end, &buflen, dentry->d_name.name,
+                               dentry->d_name.len) != 0) ||
+                   (prepend(&end, &buflen, "/", 1) != 0))
                        goto Elong;
-               end -= namelen;
-               memcpy(end, dentry->d_name.name, namelen);
-               *--end = '/';
                retval = end;
                dentry = parent;
        }
@@ -1834,38 +1823,122 @@ static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
        return retval;
 
 global_root:
-       namelen = dentry->d_name.len;
-       buflen -= namelen;
-       if (buflen < 0)
+       retval += 1;    /* hit the slash */
+       if (prepend(&retval, &buflen, dentry->d_name.name,
+                   dentry->d_name.len) != 0)
                goto Elong;
-       retval -= namelen-1;    /* hit the slash */
-       memcpy(retval, dentry->d_name.name, namelen);
+       root->mnt = vfsmnt;
+       root->dentry = dentry;
        return retval;
 Elong:
        return ERR_PTR(-ENAMETOOLONG);
 }
 
-/* write full pathname into buffer and return start of pathname */
-char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
-                               char *buf, int buflen)
+/**
+ * d_path - return the path of a dentry
+ * @path: path to report
+ * @buf: buffer to return value in
+ * @buflen: buffer length
+ *
+ * Convert a dentry into an ASCII path name. If the entry has been deleted
+ * the string " (deleted)" is appended. Note that this is ambiguous.
+ *
+ * Returns the buffer or an error code if the path was too long.
+ *
+ * "buflen" should be positive. Caller holds the dcache_lock.
+ */
+char *d_path(struct path *path, char *buf, int buflen)
 {
        char *res;
-       struct vfsmount *rootmnt;
-       struct dentry *root;
+       struct path root;
+       struct path tmp;
+
+       /*
+        * We have various synthetic filesystems that never get mounted.  On
+        * these filesystems dentries are never used for lookup purposes, and
+        * thus don't need to be hashed.  They also don't need a name until a
+        * user wants to identify the object in /proc/pid/fd/.  The little hack
+        * below allows us to generate a name for these objects on demand:
+        */
+       if (path->dentry->d_op && path->dentry->d_op->d_dname)
+               return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
 
        read_lock(&current->fs->lock);
-       rootmnt = mntget(current->fs->rootmnt);
-       root = dget(current->fs->root);
+       root = current->fs->root;
+       path_get(&root);
        read_unlock(&current->fs->lock);
        spin_lock(&dcache_lock);
-       res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen);
+       tmp = root;
+       res = __d_path(path, &tmp, buf, buflen);
        spin_unlock(&dcache_lock);
-       dput(root);
-       mntput(rootmnt);
+       path_put(&root);
        return res;
 }
 
 /*
+ * Helper function for dentry_operations.d_dname() members
+ */
+char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen,
+                       const char *fmt, ...)
+{
+       va_list args;
+       char temp[64];
+       int sz;
+
+       va_start(args, fmt);
+       sz = vsnprintf(temp, sizeof(temp), fmt, args) + 1;
+       va_end(args);
+
+       if (sz > sizeof(temp) || sz > buflen)
+               return ERR_PTR(-ENAMETOOLONG);
+
+       buffer += buflen - sz;
+       return memcpy(buffer, temp, sz);
+}
+
+/*
+ * Write full pathname from the root of the filesystem into the buffer.
+ */
+char *dentry_path(struct dentry *dentry, char *buf, int buflen)
+{
+       char *end = buf + buflen;
+       char *retval;
+
+       spin_lock(&dcache_lock);
+       prepend(&end, &buflen, "\0", 1);
+       if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
+               (prepend(&end, &buflen, "//deleted", 9) != 0))
+                       goto Elong;
+       if (buflen < 1)
+               goto Elong;
+       /* Get '/' right */
+       retval = end-1;
+       *retval = '/';
+
+       for (;;) {
+               struct dentry *parent;
+               if (IS_ROOT(dentry))
+                       break;
+
+               parent = dentry->d_parent;
+               prefetch(parent);
+
+               if ((prepend(&end, &buflen, dentry->d_name.name,
+                               dentry->d_name.len) != 0) ||
+                   (prepend(&end, &buflen, "/", 1) != 0))
+                       goto Elong;
+
+               retval = end;
+               dentry = parent;
+       }
+       spin_unlock(&dcache_lock);
+       return retval;
+Elong:
+       spin_unlock(&dcache_lock);
+       return ERR_PTR(-ENAMETOOLONG);
+}
+
+/*
  * NOTE! The user-level library version returns a
  * character pointer. The kernel system call just
  * returns the length of the buffer filled (which
@@ -1886,28 +1959,28 @@ char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
 asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
 {
        int error;
-       struct vfsmount *pwdmnt, *rootmnt;
-       struct dentry *pwd, *root;
+       struct path pwd, root;
        char *page = (char *) __get_free_page(GFP_USER);
 
        if (!page)
                return -ENOMEM;
 
        read_lock(&current->fs->lock);
-       pwdmnt = mntget(current->fs->pwdmnt);
-       pwd = dget(current->fs->pwd);
-       rootmnt = mntget(current->fs->rootmnt);
-       root = dget(current->fs->root);
+       pwd = current->fs->pwd;
+       path_get(&pwd);
+       root = current->fs->root;
+       path_get(&root);
        read_unlock(&current->fs->lock);
 
        error = -ENOENT;
        /* Has the current directory has been unlinked? */
        spin_lock(&dcache_lock);
-       if (pwd->d_parent == pwd || !d_unhashed(pwd)) {
+       if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) {
                unsigned long len;
+               struct path tmp = root;
                char * cwd;
 
-               cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE);
+               cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE);
                spin_unlock(&dcache_lock);
 
                error = PTR_ERR(cwd);
@@ -1925,10 +1998,8 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
                spin_unlock(&dcache_lock);
 
 out:
-       dput(pwd);
-       mntput(pwdmnt);
-       dput(root);
-       mntput(rootmnt);
+       path_put(&pwd);
+       path_put(&root);
        free_page((unsigned long) page);
        return error;
 }
@@ -2073,7 +2144,7 @@ static void __init dcache_init_early(void)
                INIT_HLIST_HEAD(&dentry_hashtable[loop]);
 }
 
-static void __init dcache_init(unsigned long mempages)
+static void __init dcache_init(void)
 {
        int loop;
 
@@ -2085,7 +2156,7 @@ static void __init dcache_init(unsigned long mempages)
        dentry_cache = KMEM_CACHE(dentry,
                SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD);
        
-       set_shrinker(DEFAULT_SEEKS, shrink_dcache_memory);
+       register_shrinker(&dcache_shrinker);
 
        /* Hash may have been set up in dcache_init_early */
        if (!hashdist)
@@ -2130,15 +2201,15 @@ void __init vfs_caches_init(unsigned long mempages)
        mempages -= reserve;
 
        names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
-                       SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+                       SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
 
        filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
-                       SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+                       SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
 
-       dcache_init(mempages);
-       inode_init(mempages);
+       dcache_init();
+       inode_init();
        files_init(mempages);
-       mnt_init(mempages);
+       mnt_init();
        bdev_cache_init();
        chrdev_init();
 }