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:
Родитель
60cc43fc88
Коммит
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 {
|
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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче