Re: [PATCH v2 3/3] ovl: lookup in inode cache first when decoding lower file handle

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, Mar 11, 2018 at 12:22 PM, Amir Goldstein <amir73il@xxxxxxxxx> wrote:
> When decoding a lower file handle, we need to check if lower file was
> copied up and indexed and if it has a whiteout index, we need to check
> if this is an unlinked but open non-dir before returning -ESTALE.
>
> To find out if this is an unlinked but open non-dir we need to lookup
> an overlay inode in inode cache by lower inode and that requires decoding
> the lower file handle before looking in inode cache.
>
> Before this change, if the lower inode turned out to be a directory, we
> may have paid an expensive cost to reconnect that lower directory for
> nothing.
>
> After this change, we start by decoding a disconnected lower dentry and
> using the lower inode for looking up an overlay inode in inode cache.
> If we find overlay inode and dentry in cache, we avoid the index lookup
> overhead. If we don't find an overlay inode and dentry in cache, then we
> only need to decode a connected lower dentry in case the lower dentry is
> a non-indexed directory.
>
> The xfstests group overlay/exportfs tests decoding overlayfs file
> handles after drop_caches with different states of the file at encode
> and decode time. Overall the tests in the group call ovl_lower_fh_to_d()
> 89 times to decode a lower file handle.
>
> Before this change, the tests called ovl_get_index_fh() 75 times and
> reconnect_one() 61 times.
> After this change, the tests call ovl_get_index_fh() 70 times and
> reconnect_one() 59 times. The 2 cases where reconnect_one() was avoided
> are cases where a non-upper directory file handle was encoded, then the
> directory removed and then file handle was decoded.
>
> To demonstrate the affect on decoding file handles with hot inode/dentry
> cache, the drop_caches call in the tests was disabled. Without
> drop_caches, there are no reconnect_one() calls at all before or after
> the change. Before the change, there are 75 calls to ovl_get_index_fh(),
> exactly as the case with drop_caches. After the change, there are only
> 10 calls to ovl_get_index_fh().
>
> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>
> ---
>  fs/overlayfs/export.c | 58 +++++++++++++++++++++++++++++----------------------
>  1 file changed, 33 insertions(+), 25 deletions(-)
>
> diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c
> index 15411350a7a1..a908da8b63c1 100644
> --- a/fs/overlayfs/export.c
> +++ b/fs/overlayfs/export.c
> @@ -703,25 +703,39 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
>         struct ovl_path *stack = &origin;
>         struct dentry *dentry = NULL;
>         struct dentry *index = NULL;
> -       struct inode *inode = NULL;
> -       bool is_deleted = false;
> +       struct inode *inode;
>         int err;
>
> -       /* First lookup indexed upper by fh */
> +       /* First lookup overlay inode in inode cache by origin fh */
> +       err = ovl_check_origin_fh(ofs, fh, false, NULL, &stack);
> +       if (err)
> +               return ERR_PTR(err);
> +
> +       if (d_is_dir(origin.dentry) ||
> +           !(origin.dentry->d_flags & DCACHE_DISCONNECTED)) {

I don't understand the above test.

If origin is a connected directory, then we have a chance of being
able to lookup the overlay inode in the icache.

If origin is a disconnected directory, then we don't have a chance,
because if overlay dentry was cached, it would hold a ref to the
connected origin.

If origin is a non-dir, then we just don't care about being disconnected or not.

So test should be

        if (!d_is_dir(origin.dentry) || !(origin.dentry->d_flags &
DCACHE_DISCONNECTED))

What am I missing?

> +               inode = ovl_lookup_inode(sb, origin.dentry, false);
> +               err = PTR_ERR(inode);
> +               if (IS_ERR(inode))
> +                       goto out_err;
> +               if (inode) {
> +                       dentry = d_find_any_alias(inode);
> +                       iput(inode);
> +                       if (dentry)
> +                               goto out;
> +               }
> +       }
> +
> +       /* Then lookup indexed upper/whiteout by origin fh */
>         if (ofs->indexdir) {
>                 index = ovl_get_index_fh(ofs, fh);
>                 err = PTR_ERR(index);
>                 if (IS_ERR(index)) {
> -                       if (err != -ESTALE)
> -                               return ERR_PTR(err);
> -
> -                       /* Found a whiteout index - treat as deleted inode */
> -                       is_deleted = true;
>                         index = NULL;
> +                       goto out_err;
>                 }
>         }
>
> -       /* Then try to get upper dir by index */
> +       /* Then try to get a connected upper dir by index */
>         if (index && d_is_dir(index)) {
>                 struct dentry *upper = ovl_index_upper(ofs, index);
>
> @@ -734,24 +748,19 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
>                 goto out;
>         }
>
> -       /* Then lookup origin by fh */
> -       err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
> -       if (err) {
> -               goto out_err;
> -       } else if (index) {
> -               err = ovl_verify_origin(index, origin.dentry, false);
> +       /* Otherwise, get a connected non-upper dir or disconnected non-dir */
> +       if (d_is_dir(origin.dentry) &&
> +           (origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
> +               dput(origin.dentry);
> +               origin.dentry = NULL;
> +               err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
>                 if (err)
>                         goto out_err;
> -       } else if (is_deleted) {
> -               /* Lookup deleted non-dir by origin inode */
> -               if (!d_is_dir(origin.dentry))
> -                       inode = ovl_lookup_inode(sb, origin.dentry, false);
> -               err = -ESTALE;
> -               if (!inode || atomic_read(&inode->i_count) == 1)
> +       }
> +       if (index) {
> +               err = ovl_verify_origin(index, origin.dentry, false);
> +               if (err)
>                         goto out_err;
> -
> -               /* Deleted but still open? */
> -               index = dget(ovl_i_dentry_upper(inode));
>         }
>
>         dentry = ovl_get_dentry(sb, NULL, &origin, index);
> @@ -759,7 +768,6 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
>  out:
>         dput(origin.dentry);
>         dput(index);
> -       iput(inode);
>         return dentry;
>
>  out_err:
> --
> 2.7.4
>
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux