diff options
| author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-01-13 15:00:22 +0000 | 
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-01-13 15:00:22 +0000 | 
| commit | 4de3a8e101150feaefa1139611a50ff37467f33e (patch) | |
| tree | daada742542518b02d7db7c5d32e715eaa5f166d /fs/dcache.c | |
| parent | 294064f58953f9964e5945424b09c51800330a83 (diff) | |
| parent | 099469502f62fbe0d7e4f0b83a2f22538367f734 (diff) | |
Merge branch 'master' into fixes
Diffstat (limited to 'fs/dcache.c')
| -rw-r--r-- | fs/dcache.c | 132 | 
1 files changed, 56 insertions, 76 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 89509b5a090..616fedff011 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -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; @@ -821,23 +822,6 @@ 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 - * 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;  | 
