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:
Al Viro 2018-04-15 18:21:47 -04:00
Родитель 60cc43fc88
Коммит ff17fa561a
1 изменённых файлов: 16 добавлений и 46 удалений

Просмотреть файл

@ -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);