Re: [PATCH 03/20] ovl: Call ovl_create_temp() without lock held.

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

 



On Fri, Jul 11, 2025 at 1:21 AM NeilBrown <neil@xxxxxxxxxx> wrote:
>
> ovl currently locks a directory or two and then performs multiple actions
> in one or both directories.  This is incompatible with proposed changes
> which will lock just the dentry of objects being acted on.
>
> This patch moves calls to ovl_create_temp() out of the locked regions and
> has it take and release the relevant lock itself.
>
> The lock that was taken before this function was called is now taken
> after.  This means that any code between where the lock was taken and
> ovl_create_temp() is now unlocked.  This necessitates the use of
> ovl_cleanup_unlocked() and the creation of ovl_lookup_upper_unlocked().
> These will be used more widely in future patches.
>
> Now that the file is created before the lock is taken for rename, we
> need to ensure the parent wasn't changed before the lock was gained.
> ovl_lock_rename_workdir() is changed to optionally receive the dentries
> that will be involved in the rename.  If either is present but has the
> wrong parent, an error is returned.
>
> Signed-off-by: NeilBrown <neil@xxxxxxxxxx>
> ---
>  fs/overlayfs/copy_up.c   |  5 ---
>  fs/overlayfs/dir.c       | 67 ++++++++++++++++++++--------------------
>  fs/overlayfs/overlayfs.h | 12 ++++++-
>  fs/overlayfs/super.c     | 11 ++++---
>  fs/overlayfs/util.c      |  7 ++++-
>  5 files changed, 58 insertions(+), 44 deletions(-)
>
> diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
> index 25be0b80a40b..eafb46686854 100644
> --- a/fs/overlayfs/copy_up.c
> +++ b/fs/overlayfs/copy_up.c
> @@ -523,7 +523,6 @@ static int ovl_create_index(struct dentry *dentry, const struct ovl_fh *fh,
>  {
>         struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
>         struct dentry *indexdir = ovl_indexdir(dentry->d_sb);
> -       struct inode *dir = d_inode(indexdir);
>         struct dentry *index = NULL;
>         struct dentry *temp = NULL;
>         struct qstr name = { };
> @@ -549,9 +548,7 @@ static int ovl_create_index(struct dentry *dentry, const struct ovl_fh *fh,
>                 return err;
>
>         ovl_start_write(dentry);
> -       inode_lock(dir);
>         temp = ovl_create_temp(ofs, indexdir, OVL_CATTR(S_IFDIR | 0));
> -       inode_unlock(dir);
>         err = PTR_ERR(temp);
>         if (IS_ERR(temp))
>                 goto free_name;
> @@ -785,9 +782,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
>                 return err;
>
>         ovl_start_write(c->dentry);
> -       inode_lock(wdir);
>         temp = ovl_create_temp(ofs, c->workdir, &cattr);
> -       inode_unlock(wdir);
>         ovl_end_write(c->dentry);
>         ovl_revert_cu_creds(&cc);
>
> diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
> index cee35d69e0e6..144e1753d0c9 100644
> --- a/fs/overlayfs/dir.c
> +++ b/fs/overlayfs/dir.c
> @@ -214,8 +214,12 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct inode *dir,
>  struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
>                                struct ovl_cattr *attr)
>  {
> -       return ovl_create_real(ofs, d_inode(workdir),
> -                              ovl_lookup_temp(ofs, workdir), attr);
> +       struct dentry *ret;
> +       inode_lock(workdir->d_inode);
> +       ret = ovl_create_real(ofs, d_inode(workdir),
> +                             ovl_lookup_temp(ofs, workdir), attr);
> +       inode_unlock(workdir->d_inode);
> +       return ret;
>  }
>
>  static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
> @@ -353,7 +357,6 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
>         struct dentry *workdir = ovl_workdir(dentry);
>         struct inode *wdir = workdir->d_inode;
>         struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
> -       struct inode *udir = upperdir->d_inode;
>         struct path upperpath;
>         struct dentry *upper;
>         struct dentry *opaquedir;
> @@ -363,28 +366,25 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
>         if (WARN_ON(!workdir))
>                 return ERR_PTR(-EROFS);
>
> -       err = ovl_lock_rename_workdir(workdir, upperdir);
> -       if (err)
> -               goto out;
> -
>         ovl_path_upper(dentry, &upperpath);
>         err = vfs_getattr(&upperpath, &stat,
>                           STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
>         if (err)
> -               goto out_unlock;
> +               goto out;
>
>         err = -ESTALE;
>         if (!S_ISDIR(stat.mode))
> -               goto out_unlock;
> +               goto out;
>         upper = upperpath.dentry;
> -       if (upper->d_parent->d_inode != udir)
> -               goto out_unlock;
>
>         opaquedir = ovl_create_temp(ofs, workdir, OVL_CATTR(stat.mode));
>         err = PTR_ERR(opaquedir);
>         if (IS_ERR(opaquedir))
> -               goto out_unlock;
> -
> +               /* workdir was unlocked, no upperdir */
> +               goto out;

Strong lint error here. Don't use multi lines (inc. comments) without {}
TBH this comment adds no clarity for me. I would remove it.

> +       err = ovl_lock_rename_workdir(workdir, opaquedir, upperdir, upper);
> +       if (err)
> +               goto out_cleanup_unlocked;

Nit: please keep the empty line after goto as it was in the code before.
I removed this empty line in other patches as well and it hurts my eyes.
I know we do not have a 100% consistent style in that regard in overlayfs code
(e.g. S_ISDIR check above), but please try to avoid changing the existing
style of code in that regard.

>         err = ovl_copy_xattr(dentry->d_sb, &upperpath, opaquedir);
>         if (err)
>                 goto out_cleanup;
> @@ -413,10 +413,10 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
>         return opaquedir;
>
>  out_cleanup:
> -       ovl_cleanup(ofs, wdir, opaquedir);
> -       dput(opaquedir);
> -out_unlock:
>         unlock_rename(workdir, upperdir);
> +out_cleanup_unlocked:
> +       ovl_cleanup_unlocked(ofs, workdir, opaquedir);
> +       dput(opaquedir);
>  out:
>         return ERR_PTR(err);
>  }
> @@ -454,15 +454,11 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
>                         return err;
>         }
>
> -       err = ovl_lock_rename_workdir(workdir, upperdir);
> -       if (err)
> -               goto out;
> -
> -       upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
> -                                dentry->d_name.len);
> +       upper = ovl_lookup_upper_unlocked(ofs, dentry->d_name.name, upperdir,
> +                                         dentry->d_name.len);
>         err = PTR_ERR(upper);
>         if (IS_ERR(upper))
> -               goto out_unlock;
> +               goto out;
>
>         err = -ESTALE;
>         if (d_is_negative(upper) || !ovl_upper_is_whiteout(ofs, upper))
> @@ -473,6 +469,10 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
>         if (IS_ERR(newdentry))
>                 goto out_dput;
>
> +       err = ovl_lock_rename_workdir(workdir, newdentry, upperdir, upper);
> +       if (err)
> +               goto out_cleanup;
> +

goto out_cleanup_unlocked here please
and leave the rest of the goto cleanup be
just like you did in ovl_clear_empty().

This looks way better than v1 patch 2 that overflowed my review context stack.
With minor nits above fixed, feel free to add:

Reviewed-by: Amir Goldstein <amir73il@xxxxxxxxx>


Thanks,
Amir.





[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