Skip to content
Snippets Groups Projects
dcache.c 59.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * fs/dcache.c
     *
     * Complete reimplementation
     * (C) 1997 Thomas Schoebel-Theuer,
     * with heavy changes by Linus Torvalds
     */
    
    /*
     * Notes on the allocation strategy:
     *
     * The dcache is a master of the icache - whenever a dcache entry
     * exists, the inode will always exist. "iput()" is done either when
     * the dcache entry is deleted or garbage collected.
     */
    
    #include <linux/syscalls.h>
    #include <linux/string.h>
    #include <linux/mm.h>
    #include <linux/fs.h>
    
    #include <linux/fsnotify.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <linux/slab.h>
    #include <linux/init.h>
    #include <linux/hash.h>
    #include <linux/cache.h>
    #include <linux/module.h>
    #include <linux/mount.h>
    #include <linux/file.h>
    #include <asm/uaccess.h>
    #include <linux/security.h>
    #include <linux/seqlock.h>
    #include <linux/swap.h>
    #include <linux/bootmem.h>
    
    #include <linux/fs_struct.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    int sysctl_vfs_cache_pressure __read_mostly = 100;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure);
    
     __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lock);
    
    __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    EXPORT_SYMBOL(dcache_lock);
    
    
    static struct kmem_cache *dentry_cache __read_mostly;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    #define DNAME_INLINE_LEN (sizeof(struct dentry)-offsetof(struct dentry,d_iname))
    
    /*
     * This is the single most critical data structure when it comes
     * to the dcache: the hashtable for lookups. Somebody should try
     * to make this good - I've just made it work.
     *
     * This hash-function tries to avoid losing too many bits of hash
     * information, yet avoid using a prime hash-size or similar.
     */
    #define D_HASHBITS     d_hash_shift
    #define D_HASHMASK     d_hash_mask
    
    
    static unsigned int d_hash_mask __read_mostly;
    static unsigned int d_hash_shift __read_mostly;
    static struct hlist_head *dentry_hashtable __read_mostly;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /* Statistics gathering. */
    struct dentry_stat_t dentry_stat = {
    	.age_limit = 45,
    };
    
    
    static void __d_free(struct dentry *dentry)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	WARN_ON(!list_empty(&dentry->d_alias));
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (dname_external(dentry))
    		kfree(dentry->d_name.name);
    	kmem_cache_free(dentry_cache, dentry); 
    }
    
    
    static void d_callback(struct rcu_head *head)
    {
    	struct dentry * dentry = container_of(head, struct dentry, d_u.d_rcu);
    	__d_free(dentry);
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * no dcache_lock, please.  The caller must decrement dentry_stat.nr_dentry
     * inside dcache_lock.
     */
    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 */
    
    Akinobu Mita's avatar
    Akinobu Mita committed
    	if (hlist_unhashed(&dentry->d_hash))
    
    		__d_free(dentry);
    	else
    		call_rcu(&dentry->d_u.d_rcu, d_callback);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * Release the dentry's inode, using the filesystem
     * d_iput() operation if defined.
     */
    
    static void dentry_iput(struct dentry * dentry)
    
    	__releases(dentry->d_lock)
    	__releases(dcache_lock)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct inode *inode = dentry->d_inode;
    	if (inode) {
    		dentry->d_inode = NULL;
    		list_del_init(&dentry->d_alias);
    		spin_unlock(&dentry->d_lock);
    		spin_unlock(&dcache_lock);
    
    		if (!inode->i_nlink)
    			fsnotify_inoderemove(inode);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (dentry->d_op && dentry->d_op->d_iput)
    			dentry->d_op->d_iput(dentry, inode);
    		else
    			iput(inode);
    	} else {
    		spin_unlock(&dentry->d_lock);
    		spin_unlock(&dcache_lock);
    	}
    }
    
    
    /*
     * dentry_lru_(add|add_tail|del|del_init) must be called with dcache_lock held.
     */
    static void dentry_lru_add(struct dentry *dentry)
    {
    	list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
    	dentry->d_sb->s_nr_dentry_unused++;
    	dentry_stat.nr_unused++;
    }
    
    static void dentry_lru_add_tail(struct dentry *dentry)
    {
    	list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru);
    	dentry->d_sb->s_nr_dentry_unused++;
    	dentry_stat.nr_unused++;
    }
    
    static void dentry_lru_del(struct dentry *dentry)
    {
    	if (!list_empty(&dentry->d_lru)) {
    		list_del(&dentry->d_lru);
    		dentry->d_sb->s_nr_dentry_unused--;
    		dentry_stat.nr_unused--;
    	}
    }
    
    static void dentry_lru_del_init(struct dentry *dentry)
    {
    	if (likely(!list_empty(&dentry->d_lru))) {
    		list_del_init(&dentry->d_lru);
    		dentry->d_sb->s_nr_dentry_unused--;
    		dentry_stat.nr_unused--;
    	}
    }
    
    
    /**
     * d_kill - kill dentry and return parent
     * @dentry: dentry to kill
     *
    
     * The dentry must already be unhashed and removed from the LRU.
    
     *
     * If this is the root of the dentry tree, return NULL.
     */
    static struct dentry *d_kill(struct dentry *dentry)
    
    	__releases(dentry->d_lock)
    	__releases(dcache_lock)
    
    {
    	struct dentry *parent;
    
    	list_del(&dentry->d_u.d_child);
    	dentry_stat.nr_dentry--;	/* For d_free, below */
    	/*drops the locks, at that point nobody can reach this dentry */
    	dentry_iput(dentry);
    
    	if (IS_ROOT(dentry))
    		parent = NULL;
    	else
    		parent = dentry->d_parent;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /* 
     * This is dput
     *
     * This is complicated by the fact that we do not want to put
     * dentries that are no longer on any hash chain on the unused
     * list: we'd much rather just get rid of them immediately.
     *
     * However, that implies that we have to traverse the dentry
     * tree upwards to the parents which might _also_ now be
     * scheduled for deletion (it may have been only waiting for
     * its last child to go away).
     *
     * This tail recursion is done by hand as we don't want to depend
     * on the compiler to always get this right (gcc generally doesn't).
     * Real recursion would eat up our stack space.
     */
    
    /*
     * dput - release a dentry
     * @dentry: dentry to release 
     *
     * Release a dentry. This will drop the usage count and if appropriate
     * call the dentry unlink method as well as removing it from the queues and
     * releasing its resources. If the parent dentries were scheduled for release
     * they too may now get deleted.
     *
     * no dcache lock, please.
     */
    
    void dput(struct dentry *dentry)
    {
    	if (!dentry)
    		return;
    
    repeat:
    	if (atomic_read(&dentry->d_count) == 1)
    		might_sleep();
    	if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
    		return;
    
    	spin_lock(&dentry->d_lock);
    	if (atomic_read(&dentry->d_count)) {
    		spin_unlock(&dentry->d_lock);
    		spin_unlock(&dcache_lock);
    		return;
    	}
    
    	/*
    	 * AV: ->d_delete() is _NOT_ allowed to block now.
    	 */
    	if (dentry->d_op && dentry->d_op->d_delete) {
    		if (dentry->d_op->d_delete(dentry))
    			goto unhash_it;
    	}
    	/* Unreachable? Get rid of it */
     	if (d_unhashed(dentry))
    		goto kill_it;
      	if (list_empty(&dentry->d_lru)) {
      		dentry->d_flags |= DCACHE_REFERENCED;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
      	}
     	spin_unlock(&dentry->d_lock);
    	spin_unlock(&dcache_lock);
    	return;
    
    unhash_it:
    	__d_drop(dentry);
    
    	/* if dentry was on the d_lru list delete it from there */
    	dentry_lru_del(dentry);
    
    	dentry = d_kill(dentry);
    	if (dentry)
    		goto repeat;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(dput);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /**
     * d_invalidate - invalidate a dentry
     * @dentry: dentry to invalidate
     *
     * Try to invalidate the dentry if it turns out to be
     * possible. If there are other dentries that can be
     * reached through this one we can't delete it and we
     * return -EBUSY. On success we return 0.
     *
     * no dcache lock.
     */
     
    int d_invalidate(struct dentry * dentry)
    {
    	/*
    	 * If it's already been dropped, return OK.
    	 */
    	spin_lock(&dcache_lock);
    	if (d_unhashed(dentry)) {
    		spin_unlock(&dcache_lock);
    		return 0;
    	}
    	/*
    	 * Check whether to do a partial shrink_dcache
    	 * to get rid of unused child entries.
    	 */
    	if (!list_empty(&dentry->d_subdirs)) {
    		spin_unlock(&dcache_lock);
    		shrink_dcache_parent(dentry);
    		spin_lock(&dcache_lock);
    	}
    
    	/*
    	 * Somebody else still using it?
    	 *
    	 * If it's a directory, we can't drop it
    	 * for fear of somebody re-populating it
    	 * with children (even though dropping it
    	 * would make it unreachable from the root,
    	 * we might still populate it if it was a
    	 * working directory or similar).
    	 */
    	spin_lock(&dentry->d_lock);
    	if (atomic_read(&dentry->d_count) > 1) {
    		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
    			spin_unlock(&dentry->d_lock);
    			spin_unlock(&dcache_lock);
    			return -EBUSY;
    		}
    	}
    
    	__d_drop(dentry);
    	spin_unlock(&dentry->d_lock);
    	spin_unlock(&dcache_lock);
    	return 0;
    }
    
    EXPORT_SYMBOL(d_invalidate);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /* This should be called _only_ with dcache_lock held */
    
    static inline struct dentry * __dget_locked(struct dentry *dentry)
    {
    	atomic_inc(&dentry->d_count);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return dentry;
    }
    
    struct dentry * dget_locked(struct dentry *dentry)
    {
    	return __dget_locked(dentry);
    }
    
    EXPORT_SYMBOL(dget_locked);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /**
     * d_find_alias - grab a hashed alias of inode
     * @inode: inode in question
     * @want_discon:  flag, used by d_splice_alias, to request
     *          that only a DISCONNECTED alias be returned.
     *
     * If inode has a hashed alias, or is a directory and has any alias,
     * acquire the reference to alias and return it. Otherwise return NULL.
     * Notice that if inode is a directory there can be only one alias and
     * it can be unhashed only if it has no children, or if it is the root
     * of a filesystem.
     *
    
     * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * any other hashed alias over that one unless @want_discon is set,
    
     * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    static struct dentry * __d_find_alias(struct inode *inode, int want_discon)
    {
    	struct list_head *head, *next, *tmp;
    	struct dentry *alias, *discon_alias=NULL;
    
    	head = &inode->i_dentry;
    	next = inode->i_dentry.next;
    	while (next != head) {
    		tmp = next;
    		next = tmp->next;
    		prefetch(next);
    		alias = list_entry(tmp, struct dentry, d_alias);
     		if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
    
    			if (IS_ROOT(alias) &&
    			    (alias->d_flags & DCACHE_DISCONNECTED))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				discon_alias = alias;
    			else if (!want_discon) {
    				__dget_locked(alias);
    				return alias;
    			}
    		}
    	}
    	if (discon_alias)
    		__dget_locked(discon_alias);
    	return discon_alias;
    }
    
    struct dentry * d_find_alias(struct inode *inode)
    {
    
    	struct dentry *de = NULL;
    
    	if (!list_empty(&inode->i_dentry)) {
    		spin_lock(&dcache_lock);
    		de = __d_find_alias(inode, 0);
    		spin_unlock(&dcache_lock);
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return de;
    }
    
    EXPORT_SYMBOL(d_find_alias);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /*
     *	Try to kill dentries associated with this inode.
     * WARNING: you must own a reference to inode.
     */
    void d_prune_aliases(struct inode *inode)
    {
    
    	struct dentry *dentry;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    restart:
    	spin_lock(&dcache_lock);
    
    	list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		spin_lock(&dentry->d_lock);
    		if (!atomic_read(&dentry->d_count)) {
    			__dget_locked(dentry);
    			__d_drop(dentry);
    			spin_unlock(&dentry->d_lock);
    			spin_unlock(&dcache_lock);
    			dput(dentry);
    			goto restart;
    		}
    		spin_unlock(&dentry->d_lock);
    	}
    	spin_unlock(&dcache_lock);
    }
    
    EXPORT_SYMBOL(d_prune_aliases);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /*
    
     * Throw away a dentry - free the inode, dput the parent.  This requires that
     * the LRU list has already been removed.
     *
    
     * 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.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    static void prune_one_dentry(struct dentry * dentry)
    
    	__releases(dentry->d_lock)
    	__releases(dcache_lock)
    	__acquires(dcache_lock)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	__d_drop(dentry);
    
    	dentry = d_kill(dentry);
    
    	/*
    	 * Prune ancestors.  Locking is simpler than in dput(),
    	 * because dcache_lock needs to be taken anyway.
    	 */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	spin_lock(&dcache_lock);
    
    	while (dentry) {
    		if (!atomic_dec_and_lock(&dentry->d_count, &dentry->d_lock))
    			return;
    
    		if (dentry->d_op && dentry->d_op->d_delete)
    			dentry->d_op->d_delete(dentry);
    
    		__d_drop(dentry);
    		dentry = d_kill(dentry);
    		spin_lock(&dcache_lock);
    	}
    
    /*
     * Shrink the dentry LRU on a given superblock.
     * @sb   : superblock to shrink dentry LRU.
     * @count: If count is NULL, we prune all dentries on superblock.
     * @flags: If flags is non-zero, we need to do special processing based on
     * which flags are set. This means we don't need to maintain multiple
     * similar copies of this loop.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	LIST_HEAD(referenced);
    	LIST_HEAD(tmp);
    	struct dentry *dentry;
    	int cnt = 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	BUG_ON(!sb);
    	BUG_ON((flags & DCACHE_REFERENCED) && count == NULL);
    	spin_lock(&dcache_lock);
    	if (count != NULL)
    		/* called from prune_dcache() and shrink_dcache_parent() */
    		cnt = *count;
    restart:
    	if (count == NULL)
    		list_splice_init(&sb->s_dentry_lru, &tmp);
    	else {
    		while (!list_empty(&sb->s_dentry_lru)) {
    			dentry = list_entry(sb->s_dentry_lru.prev,
    					struct dentry, d_lru);
    			BUG_ON(dentry->d_sb != sb);
    
    			spin_lock(&dentry->d_lock);
    			/*
    			 * 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)) {
    				dentry->d_flags &= ~DCACHE_REFERENCED;
    
    				list_move(&dentry->d_lru, &referenced);
    
    				spin_unlock(&dentry->d_lock);
    			} else {
    				list_move_tail(&dentry->d_lru, &tmp);
    				spin_unlock(&dentry->d_lock);
    				cnt--;
    				if (!cnt)
    					break;
    
    			cond_resched_lock(&dcache_lock);
    
    	}
    	while (!list_empty(&tmp)) {
    		dentry = list_entry(tmp.prev, struct dentry, d_lru);
    		dentry_lru_del_init(dentry);
    		spin_lock(&dentry->d_lock);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/*
    		 * We found an inuse dentry which was not removed from
    
    		 * the LRU because of laziness during lookup.  Do not free
    		 * it - just keep it off the LRU list.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		 */
    
    		if (atomic_read(&dentry->d_count)) {
    			spin_unlock(&dentry->d_lock);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			continue;
    		}
    
    		prune_one_dentry(dentry);
    		/* dentry->d_lock was dropped in prune_one_dentry() */
    		cond_resched_lock(&dcache_lock);
    	}
    	if (count == NULL && !list_empty(&sb->s_dentry_lru))
    		goto restart;
    	if (count != NULL)
    		*count = cnt;
    	if (!list_empty(&referenced))
    		list_splice(&referenced, &sb->s_dentry_lru);
    	spin_unlock(&dcache_lock);
    }
    
    /**
     * prune_dcache - shrink the dcache
     * @count: number of entries to try to free
     *
     * Shrink the dcache. This is done when we need more memory, or simply when we
     * need to unmount something (at which point we need to unuse all dentries).
     *
     * This function may fail to free any resources if all the dentries are in use.
     */
    static void prune_dcache(int count)
    {
    	struct super_block *sb;
    	int w_count;
    	int unused = dentry_stat.nr_unused;
    	int prune_ratio;
    	int pruned;
    
    	if (unused == 0 || count == 0)
    		return;
    	spin_lock(&dcache_lock);
    restart:
    	if (count >= unused)
    		prune_ratio = 1;
    	else
    		prune_ratio = unused / count;
    	spin_lock(&sb_lock);
    	list_for_each_entry(sb, &super_blocks, s_list) {
    		if (sb->s_nr_dentry_unused == 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			continue;
    
    		sb->s_count++;
    		/* Now, we reclaim unused dentrins with fairness.
    		 * We reclaim them same percentage from each superblock.
    		 * We calculate number of dentries to scan on this sb
    		 * as follows, but the implementation is arranged to avoid
    		 * overflows:
    		 * number of dentries to scan on this sb =
    		 * count * (number of dentries on this sb /
    		 * number of dentries in the machine)
    
    		spin_unlock(&sb_lock);
    		if (prune_ratio != 1)
    			w_count = (sb->s_nr_dentry_unused / prune_ratio) + 1;
    		else
    			w_count = sb->s_nr_dentry_unused;
    		pruned = w_count;
    
    		 * 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.
    
    		if (down_read_trylock(&sb->s_umount)) {
    			if ((sb->s_root != NULL) &&
    			    (!list_empty(&sb->s_dentry_lru))) {
    				spin_unlock(&dcache_lock);
    				__shrink_dcache_sb(sb, &w_count,
    						DCACHE_REFERENCED);
    				pruned -= w_count;
    				spin_lock(&dcache_lock);
    
    		spin_lock(&sb_lock);
    		count -= pruned;
    
    		 * restart only when sb is no longer on the list and
    		 * we have more work to do.
    
    		if (__put_super_and_need_restart(sb) && count > 0) {
    			spin_unlock(&sb_lock);
    			goto restart;
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	spin_unlock(&dcache_lock);
    }
    
    /**
     * shrink_dcache_sb - shrink dcache for a superblock
     * @sb: superblock
     *
     * Shrink the dcache for the specified super block. This
     * is used to free the dcache before unmounting a file
     * system
     */
    void shrink_dcache_sb(struct super_block * sb)
    {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(shrink_dcache_sb);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /*
     * destroy a single subtree of dentries for unmount
     * - see the comments on shrink_dcache_for_umount() for a description of the
     *   locking
     */
    static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
    {
    	struct dentry *parent;
    
    	unsigned detached = 0;
    
    
    	BUG_ON(!IS_ROOT(dentry));
    
    	/* detach this root from the system */
    	spin_lock(&dcache_lock);
    
    	__d_drop(dentry);
    	spin_unlock(&dcache_lock);
    
    	for (;;) {
    		/* descend to the first leaf in the current subtree */
    		while (!list_empty(&dentry->d_subdirs)) {
    			struct dentry *loop;
    
    			/* this is a branch with children - detach all of them
    			 * from the system in one go */
    			spin_lock(&dcache_lock);
    			list_for_each_entry(loop, &dentry->d_subdirs,
    					    d_u.d_child) {
    
    				__d_drop(loop);
    				cond_resched_lock(&dcache_lock);
    			}
    			spin_unlock(&dcache_lock);
    
    			/* move to the first child */
    			dentry = list_entry(dentry->d_subdirs.next,
    					    struct dentry, d_u.d_child);
    		}
    
    		/* consume the dentries from this leaf up through its parents
    		 * until we find one with children or run out altogether */
    		do {
    			struct inode *inode;
    
    			if (atomic_read(&dentry->d_count) != 0) {
    				printk(KERN_ERR
    				       "BUG: Dentry %p{i=%lx,n=%s}"
    				       " still in use (%d)"
    				       " [unmount of %s %s]\n",
    				       dentry,
    				       dentry->d_inode ?
    				       dentry->d_inode->i_ino : 0UL,
    				       dentry->d_name.name,
    				       atomic_read(&dentry->d_count),
    				       dentry->d_sb->s_type->name,
    				       dentry->d_sb->s_id);
    				BUG();
    			}
    
    
    
    			inode = dentry->d_inode;
    			if (inode) {
    				dentry->d_inode = NULL;
    				list_del_init(&dentry->d_alias);
    				if (dentry->d_op && dentry->d_op->d_iput)
    					dentry->d_op->d_iput(dentry, inode);
    				else
    					iput(inode);
    			}
    
    			d_free(dentry);
    
    			/* finished when we fall off the top of the tree,
    			 * otherwise we ascend to the parent and move to the
    			 * next sibling if there is one */
    			if (!parent)
    
    
    			dentry = parent;
    
    		} while (list_empty(&dentry->d_subdirs));
    
    		dentry = list_entry(dentry->d_subdirs.next,
    				    struct dentry, d_u.d_child);
    	}
    
    out:
    	/* several dentries were freed, need to correct nr_dentry */
    	spin_lock(&dcache_lock);
    	dentry_stat.nr_dentry -= detached;
    	spin_unlock(&dcache_lock);
    
    }
    
    /*
     * destroy the dentries attached to a superblock on unmounting
     * - we don't need to use dentry->d_lock, and only need dcache_lock when
     *   removing the dentry from the system lists and hashes because:
     *   - the superblock is detached from all mountings and open files, so the
     *     dentry trees will not be rearranged by the VFS
     *   - s_umount is write-locked, so the memory pressure shrinker will ignore
     *     any dentries belonging to this superblock that it comes across
     *   - the filesystem itself is no longer permitted to rearrange the dentries
     *     in this superblock
     */
    void shrink_dcache_for_umount(struct super_block *sb)
    {
    	struct dentry *dentry;
    
    	if (down_read_trylock(&sb->s_umount))
    		BUG();
    
    	dentry = sb->s_root;
    	sb->s_root = NULL;
    	atomic_dec(&dentry->d_count);
    	shrink_dcache_for_umount_subtree(dentry);
    
    	while (!hlist_empty(&sb->s_anon)) {
    		dentry = hlist_entry(sb->s_anon.first, struct dentry, d_hash);
    		shrink_dcache_for_umount_subtree(dentry);
    	}
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * Search for at least 1 mount point in the dentry's subdirs.
     * We descend to the next level whenever the d_subdirs
     * list is non-empty and continue searching.
     */
     
    /**
     * have_submounts - check for mounts over a dentry
     * @parent: dentry to check.
     *
     * Return true if the parent or its subdirectories contain
     * a mount point
     */
     
    int have_submounts(struct dentry *parent)
    {
    	struct dentry *this_parent = parent;
    	struct list_head *next;
    
    	spin_lock(&dcache_lock);
    	if (d_mountpoint(parent))
    		goto positive;
    repeat:
    	next = this_parent->d_subdirs.next;
    resume:
    	while (next != &this_parent->d_subdirs) {
    		struct list_head *tmp = next;
    
    		struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		next = tmp->next;
    		/* Have we found a mount point ? */
    		if (d_mountpoint(dentry))
    			goto positive;
    		if (!list_empty(&dentry->d_subdirs)) {
    			this_parent = dentry;
    			goto repeat;
    		}
    	}
    	/*
    	 * All done at this level ... ascend and resume the search.
    	 */
    	if (this_parent != parent) {
    
    		next = this_parent->d_u.d_child.next;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		this_parent = this_parent->d_parent;
    		goto resume;
    	}
    	spin_unlock(&dcache_lock);
    	return 0; /* No mount points found in tree */
    positive:
    	spin_unlock(&dcache_lock);
    	return 1;
    }
    
    EXPORT_SYMBOL(have_submounts);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /*
     * Search the dentry child list for the specified parent,
     * and move any unused dentries to the end of the unused
     * list for prune_dcache(). We descend to the next level
     * whenever the d_subdirs list is non-empty and continue
     * searching.
     *
     * It returns zero iff there are no unused children,
     * otherwise  it returns the number of children moved to
     * the end of the unused list. This may not be the total
     * number of unused children, because select_parent can
     * drop the lock and return early due to latency
     * constraints.
     */
    static int select_parent(struct dentry * parent)
    {
    	struct dentry *this_parent = parent;
    	struct list_head *next;
    	int found = 0;
    
    	spin_lock(&dcache_lock);
    repeat:
    	next = this_parent->d_subdirs.next;
    resume:
    	while (next != &this_parent->d_subdirs) {
    		struct list_head *tmp = next;
    
    		struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		next = tmp->next;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		/* 
    		 * move only zero ref count dentries to the end 
    		 * of the unused list for prune_dcache
    		 */
    		if (!atomic_read(&dentry->d_count)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			found++;
    		}
    
    		/*
    		 * We can return to the caller if we have found some (this
    		 * ensures forward progress). We'll be coming back to find
    		 * the rest.
    		 */
    		if (found && need_resched())
    			goto out;
    
    		/*
    		 * Descend a level if the d_subdirs list is non-empty.
    		 */
    		if (!list_empty(&dentry->d_subdirs)) {
    			this_parent = dentry;
    			goto repeat;
    		}
    	}
    	/*
    	 * All done at this level ... ascend and resume the search.
    	 */
    	if (this_parent != parent) {
    
    		next = this_parent->d_u.d_child.next;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		this_parent = this_parent->d_parent;
    		goto resume;
    	}
    out:
    	spin_unlock(&dcache_lock);
    	return found;
    }
    
    /**
     * shrink_dcache_parent - prune dcache
     * @parent: parent of entries to prune
     *
     * 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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int found;
    
    	while ((found = select_parent(parent)) != 0)
    
    		__shrink_dcache_sb(sb, &found, 0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(shrink_dcache_parent);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /*
     * Scan `nr' dentries and return the number which remain.
     *
     * We need to avoid reentering the filesystem if the caller is performing a
     * GFP_NOFS allocation attempt.  One example deadlock is:
     *
     * ext2_new_block->getblk->GFP->shrink_dcache_memory->prune_dcache->
     * prune_one_dentry->dput->dentry_iput->iput->inode->i_sb->s_op->put_inode->
     * ext2_discard_prealloc->ext2_free_blocks->lock_super->DEADLOCK.
     *
     * In this case we return -1 to tell the caller that we baled.
     */
    
    Al Viro's avatar
    Al Viro committed
    static int shrink_dcache_memory(int nr, gfp_t gfp_mask)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	if (nr) {
    		if (!(gfp_mask & __GFP_FS))
    			return -1;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    	return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
    }
    
    
    static struct shrinker dcache_shrinker = {
    	.shrink = shrink_dcache_memory,
    	.seeks = DEFAULT_SEEKS,
    };
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /**
     * d_alloc	-	allocate a dcache entry
     * @parent: parent of entry to allocate
     * @name: qstr of the name
     *
     * Allocates a dentry. It returns %NULL if there is insufficient memory
     * available. On a success the dentry is returned. The name passed in is
     * copied and the copy passed in may be reused after this call.
     */
     
    struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
    {
    	struct dentry *dentry;
    	char *dname;
    
    
    	dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!dentry)
    		return NULL;
    
    	if (name->len > DNAME_INLINE_LEN-1) {
    		dname = kmalloc(name->len + 1, GFP_KERNEL);
    		if (!dname) {
    			kmem_cache_free(dentry_cache, dentry); 
    			return NULL;
    		}
    	} else  {
    		dname = dentry->d_iname;
    	}	
    	dentry->d_name.name = dname;
    
    	dentry->d_name.len = name->len;
    	dentry->d_name.hash = name->hash;
    	memcpy(dname, name->name, name->len);
    	dname[name->len] = 0;
    
    	atomic_set(&dentry->d_count, 1);
    	dentry->d_flags = DCACHE_UNHASHED;
    	spin_lock_init(&dentry->d_lock);
    	dentry->d_inode = NULL;
    	dentry->d_parent = NULL;
    	dentry->d_sb = NULL;
    	dentry->d_op = NULL;
    	dentry->d_fsdata = NULL;
    	dentry->d_mounted = 0;
    	INIT_HLIST_NODE(&dentry->d_hash);
    	INIT_LIST_HEAD(&dentry->d_lru);
    	INIT_LIST_HEAD(&dentry->d_subdirs);
    	INIT_LIST_HEAD(&dentry->d_alias);
    
    	if (parent) {
    		dentry->d_parent = dget(parent);
    		dentry->d_sb = parent->d_sb;
    	} else {
    
    		INIT_LIST_HEAD(&dentry->d_u.d_child);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	spin_lock(&dcache_lock);
    	if (parent)
    
    		list_add(&dentry->d_u.d_child, &parent->d_subdirs);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	dentry_stat.nr_dentry++;
    	spin_unlock(&dcache_lock);
    
    	return dentry;
    }
    
    EXPORT_SYMBOL(d_alloc);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    struct dentry *d_alloc_name(struct dentry *parent, const char *name)
    {
    	struct qstr q;
    
    	q.name = name;
    	q.len = strlen(name);
    	q.hash = full_name_hash(q.name, q.len);
    	return d_alloc(parent, &q);
    }
    
    EXPORT_SYMBOL(d_alloc_name);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /* the caller must hold dcache_lock */
    static void __d_instantiate(struct dentry *dentry, struct inode *inode)
    {
    	if (inode)
    		list_add(&dentry->d_alias, &inode->i_dentry);
    	dentry->d_inode = inode;
    	fsnotify_d_instantiate(dentry, inode);
    }