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
					
				
							
								
								
									
										62
									
								
								fs/dcache.c
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								fs/dcache.c
									
									
									
									
									
								
							| @ -1542,78 +1542,48 @@ void shrink_dcache_for_umount(struct super_block *sb) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct detach_data { | ||||
| 	struct select_data select; | ||||
| 	struct dentry *mountpoint; | ||||
| }; | ||||
| static enum d_walk_ret detach_and_collect(void *_data, struct dentry *dentry) | ||||
| static enum d_walk_ret find_submount(void *_data, struct dentry *dentry) | ||||
| { | ||||
| 	struct detach_data *data = _data; | ||||
| 
 | ||||
| 	struct dentry **victim = _data; | ||||
| 	if (d_mountpoint(dentry)) { | ||||
| 		__dget_dlock(dentry); | ||||
| 		data->mountpoint = dentry; | ||||
| 		*victim = dentry; | ||||
| 		return D_WALK_QUIT; | ||||
| 	} | ||||
| 
 | ||||
| 	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); | ||||
| 	return D_WALK_CONTINUE; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * d_invalidate - detach submounts, prune dcache, 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) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * If it's already been dropped, return OK. | ||||
| 	 */ | ||||
| 	bool had_submounts = false; | ||||
| 	spin_lock(&dentry->d_lock); | ||||
| 	if (d_unhashed(dentry)) { | ||||
| 		spin_unlock(&dentry->d_lock); | ||||
| 		return; | ||||
| 	} | ||||
| 	__d_drop(dentry); | ||||
| 	spin_unlock(&dentry->d_lock); | ||||
| 
 | ||||
| 	/* Negative dentries can be dropped without further checks */ | ||||
| 	if (!dentry->d_inode) { | ||||
| 		d_drop(dentry); | ||||
| 	if (!dentry->d_inode) | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	shrink_dcache_parent(dentry); | ||||
| 	for (;;) { | ||||
| 		struct detach_data data; | ||||
| 
 | ||||
| 		data.mountpoint = NULL; | ||||
| 		INIT_LIST_HEAD(&data.select.dispose); | ||||
| 		data.select.start = 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) | ||||
| 		struct dentry *victim = NULL; | ||||
| 		d_walk(dentry, &victim, find_submount, NULL); | ||||
| 		if (!victim) { | ||||
| 			if (had_submounts) | ||||
| 				shrink_dcache_parent(dentry); | ||||
| 			return; | ||||
| 
 | ||||
| 		if (data.mountpoint) { | ||||
| 			detach_mounts(data.mountpoint); | ||||
| 			dput(data.mountpoint); | ||||
| 		} | ||||
| 		had_submounts = true; | ||||
| 		detach_mounts(victim); | ||||
| 		dput(victim); | ||||
| 	} | ||||
| } | ||||
| EXPORT_SYMBOL(d_invalidate); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user