diff options
Diffstat (limited to 'fs/dcache.c')
| -rw-r--r-- | fs/dcache.c | 83 | 
1 files changed, 36 insertions, 47 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index be18598c7fd..a88948b8bd1 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -301,6 +301,27 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)  	return parent;  } +/* + * Unhash a dentry without inserting an RCU walk barrier or checking that + * dentry->d_lock is locked.  The caller must take care of that, if + * appropriate. + */ +static void __d_shrink(struct dentry *dentry) +{ +	if (!d_unhashed(dentry)) { +		struct hlist_bl_head *b; +		if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED)) +			b = &dentry->d_sb->s_anon; +		else +			b = d_hash(dentry->d_parent, dentry->d_name.hash); + +		hlist_bl_lock(b); +		__hlist_bl_del(&dentry->d_hash); +		dentry->d_hash.pprev = NULL; +		hlist_bl_unlock(b); +	} +} +  /**   * d_drop - drop a dentry   * @dentry: dentry to drop @@ -319,17 +340,7 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)  void __d_drop(struct dentry *dentry)  {  	if (!d_unhashed(dentry)) { -		struct hlist_bl_head *b; -		if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED)) -			b = &dentry->d_sb->s_anon; -		else -			b = d_hash(dentry->d_parent, dentry->d_name.hash); - -		hlist_bl_lock(b); -		__hlist_bl_del(&dentry->d_hash); -		dentry->d_hash.pprev = NULL; -		hlist_bl_unlock(b); - +		__d_shrink(dentry);  		dentry_rcuwalk_barrier(dentry);  	}  } @@ -784,6 +795,7 @@ relock:  /**   * 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 @@ -828,44 +840,24 @@ EXPORT_SYMBOL(shrink_dcache_sb);  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(&dentry->d_lock); -	dentry_lru_del(dentry); -	__d_drop(dentry); -	spin_unlock(&dentry->d_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(&dentry->d_lock); -			list_for_each_entry(loop, &dentry->d_subdirs, -					    d_u.d_child) { -				spin_lock_nested(&loop->d_lock, -						DENTRY_D_LOCK_NESTED); -				dentry_lru_del(loop); -				__d_drop(loop); -				spin_unlock(&loop->d_lock); -			} -			spin_unlock(&dentry->d_lock); - -			/* move to the first child */ +		while (!list_empty(&dentry->d_subdirs))  			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; +			/* detach from the system */ +			dentry_lru_del(dentry); +			__d_shrink(dentry); +  			if (dentry->d_count != 0) {  				printk(KERN_ERR  				       "BUG: Dentry %p{i=%lx,n=%s}" @@ -886,14 +878,10 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)  				list_del(&dentry->d_u.d_child);  			} else {  				parent = dentry->d_parent; -				spin_lock(&parent->d_lock);  				parent->d_count--;  				list_del(&dentry->d_u.d_child); -				spin_unlock(&parent->d_lock);  			} -			detached++; -  			inode = dentry->d_inode;  			if (inode) {  				dentry->d_inode = NULL; @@ -938,9 +926,7 @@ void shrink_dcache_for_umount(struct super_block *sb)  	dentry = sb->s_root;  	sb->s_root = NULL; -	spin_lock(&dentry->d_lock);  	dentry->d_count--; -	spin_unlock(&dentry->d_lock);  	shrink_dcache_for_umount_subtree(dentry);  	while (!hlist_bl_empty(&sb->s_anon)) { @@ -1743,7 +1729,7 @@ seqretry:  		 */  		if (read_seqcount_retry(&dentry->d_seq, *seq))  			goto seqretry; -		if (parent->d_flags & DCACHE_OP_COMPARE) { +		if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) {  			if (parent->d_op->d_compare(parent, *inode,  						dentry, i,  						tlen, tname, name)) @@ -2138,8 +2124,9 @@ static void dentry_unlock_parents_for_move(struct dentry *dentry,   * @target: new dentry   *   * Update the dcache to reflect the move of a file name. Negative - * dcache entries should not be moved in this way.  Caller hold - * rename_lock. + * dcache entries should not be moved in this way. Caller must hold + * rename_lock, the i_mutex of the source and target directories, + * and the sb->s_vfs_rename_mutex if they differ. See lock_rename().   */  static void __d_move(struct dentry * dentry, struct dentry * target)  { @@ -2202,7 +2189,8 @@ static void __d_move(struct dentry * dentry, struct dentry * target)   * @target: new dentry   *   * Update the dcache to reflect the move of a file name. Negative - * dcache entries should not be moved in this way. + * dcache entries should not be moved in this way. See the locking + * requirements for __d_move.   */  void d_move(struct dentry *dentry, struct dentry *target)  { @@ -2320,7 +2308,8 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon)   * @inode: inode to bind to the dentry, to which aliases may be attached   *   * Introduces an dentry into the tree, substituting an extant disconnected - * root directory alias in its place if there is one + * root directory alias in its place if there is one. Caller must hold the + * i_mutex of the parent directory.   */  struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode)  {  |