d_invalidate(): unhash immediately
authorAl Viro <viro@zeniv.linux.org.uk>
Sun, 15 Apr 2018 22:21:47 +0000 (18:21 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 16 Apr 2018 03:36:57 +0000 (23:36 -0400)
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>
fs/dcache.c

index 86d2de63461e1550efe205643d66800dd12f15ac..449d0f895e6fbd907ce1f3664e0e8f413d4fada7 100644 (file)
@@ -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);