forked from mirrors/linux
		
	d_invalidate(): unhash immediately
Once that is done, we can just hunt mountpoints down one by one; no new mountpoints can be added from now on, so we don't need anything tricky in finish() callback, etc. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
		
							parent
							
								
									60cc43fc88
								
							
						
					
					
						commit
						ff17fa561a
					
				
					 1 changed files with 16 additions and 46 deletions
				
			
		
							
								
								
									
										62
									
								
								fs/dcache.c
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								fs/dcache.c
									
									
									
									
									
								
							| 
						 | 
					@ -1542,78 +1542,48 @@ void shrink_dcache_for_umount(struct super_block *sb)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct detach_data {
 | 
					static enum d_walk_ret find_submount(void *_data, struct dentry *dentry)
 | 
				
			||||||
	struct select_data select;
 | 
					 | 
				
			||||||
	struct dentry *mountpoint;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
static enum d_walk_ret detach_and_collect(void *_data, struct dentry *dentry)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct detach_data *data = _data;
 | 
						struct dentry **victim = _data;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (d_mountpoint(dentry)) {
 | 
						if (d_mountpoint(dentry)) {
 | 
				
			||||||
		__dget_dlock(dentry);
 | 
							__dget_dlock(dentry);
 | 
				
			||||||
		data->mountpoint = dentry;
 | 
							*victim = dentry;
 | 
				
			||||||
		return D_WALK_QUIT;
 | 
							return D_WALK_QUIT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return D_WALK_CONTINUE;
 | 
				
			||||||
	return select_collect(&data->select, dentry);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void check_and_drop(void *_data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct detach_data *data = _data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!data->mountpoint && list_empty(&data->select.dispose))
 | 
					 | 
				
			||||||
		__d_drop(data->select.start);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * d_invalidate - detach submounts, prune dcache, and drop
 | 
					 * d_invalidate - detach submounts, prune dcache, and drop
 | 
				
			||||||
 * @dentry: dentry to invalidate (aka detach, prune and drop)
 | 
					 * @dentry: dentry to invalidate (aka detach, prune and drop)
 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * no dcache lock.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The final d_drop is done as an atomic operation relative to
 | 
					 | 
				
			||||||
 * rename_lock ensuring there are no races with d_set_mounted.  This
 | 
					 | 
				
			||||||
 * ensures there are no unhashed dentries on the path to a mountpoint.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void d_invalidate(struct dentry *dentry)
 | 
					void d_invalidate(struct dentry *dentry)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/*
 | 
						bool had_submounts = false;
 | 
				
			||||||
	 * If it's already been dropped, return OK.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	spin_lock(&dentry->d_lock);
 | 
						spin_lock(&dentry->d_lock);
 | 
				
			||||||
	if (d_unhashed(dentry)) {
 | 
						if (d_unhashed(dentry)) {
 | 
				
			||||||
		spin_unlock(&dentry->d_lock);
 | 
							spin_unlock(&dentry->d_lock);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						__d_drop(dentry);
 | 
				
			||||||
	spin_unlock(&dentry->d_lock);
 | 
						spin_unlock(&dentry->d_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Negative dentries can be dropped without further checks */
 | 
						/* Negative dentries can be dropped without further checks */
 | 
				
			||||||
	if (!dentry->d_inode) {
 | 
						if (!dentry->d_inode)
 | 
				
			||||||
		d_drop(dentry);
 | 
					 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shrink_dcache_parent(dentry);
 | 
				
			||||||
	for (;;) {
 | 
						for (;;) {
 | 
				
			||||||
		struct detach_data data;
 | 
							struct dentry *victim = NULL;
 | 
				
			||||||
 | 
							d_walk(dentry, &victim, find_submount, NULL);
 | 
				
			||||||
		data.mountpoint = NULL;
 | 
							if (!victim) {
 | 
				
			||||||
		INIT_LIST_HEAD(&data.select.dispose);
 | 
								if (had_submounts)
 | 
				
			||||||
		data.select.start = dentry;
 | 
									shrink_dcache_parent(dentry);
 | 
				
			||||||
		data.select.found = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		d_walk(dentry, &data, detach_and_collect, check_and_drop);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!list_empty(&data.select.dispose))
 | 
					 | 
				
			||||||
			shrink_dentry_list(&data.select.dispose);
 | 
					 | 
				
			||||||
		else if (!data.mountpoint)
 | 
					 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (data.mountpoint) {
 | 
					 | 
				
			||||||
			detach_mounts(data.mountpoint);
 | 
					 | 
				
			||||||
			dput(data.mountpoint);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							had_submounts = true;
 | 
				
			||||||
 | 
							detach_mounts(victim);
 | 
				
			||||||
 | 
							dput(victim);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(d_invalidate);
 | 
					EXPORT_SYMBOL(d_invalidate);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue