On Wed, Jul 16, 2025 at 2:47 AM NeilBrown <neil@xxxxxxxxxx> wrote: > > ovl_whiteout() relies on the workdir i_rwsem to provide exclusive access > to ofs->whiteout which it manipulates. Rather than depending on this, > add a new mutex, "whiteout_lock" to explicitly provide the required > locking. Use guard(mutex) for this so that we can return without > needing to explicitly unlock. > > Then take the lock on workdir only when needed - to lookup the temp name > and to do the whiteout or link. > > Signed-off-by: NeilBrown <neil@xxxxxxxxxx> Reviewed-by: Amir Goldstein <amir73il@xxxxxxxxx> > --- > fs/overlayfs/dir.c | 44 ++++++++++++++++++++++------------------ > fs/overlayfs/ovl_entry.h | 1 + > fs/overlayfs/params.c | 2 ++ > 3 files changed, 27 insertions(+), 20 deletions(-) > > diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c > index 6a70faeee6fa..7eb806a4e5f8 100644 > --- a/fs/overlayfs/dir.c > +++ b/fs/overlayfs/dir.c > @@ -84,41 +84,45 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs) > struct dentry *workdir = ofs->workdir; > struct inode *wdir = workdir->d_inode; > > - inode_lock_nested(wdir, I_MUTEX_PARENT); > + guard(mutex)(&ofs->whiteout_lock); > + > if (!ofs->whiteout) { > + inode_lock_nested(wdir, I_MUTEX_PARENT); > whiteout = ovl_lookup_temp(ofs, workdir); > - if (IS_ERR(whiteout)) > - goto out; > - > - err = ovl_do_whiteout(ofs, wdir, whiteout); > - if (err) { > - dput(whiteout); > - whiteout = ERR_PTR(err); > - goto out; > + if (!IS_ERR(whiteout)) { > + err = ovl_do_whiteout(ofs, wdir, whiteout); > + if (err) { > + dput(whiteout); > + whiteout = ERR_PTR(err); > + } > } > + inode_unlock(wdir); > + if (IS_ERR(whiteout)) > + return whiteout; > ofs->whiteout = whiteout; > } > > if (!ofs->no_shared_whiteout) { > + inode_lock_nested(wdir, I_MUTEX_PARENT); > whiteout = ovl_lookup_temp(ofs, workdir); > - if (IS_ERR(whiteout)) > - goto out; > - > - err = ovl_do_link(ofs, ofs->whiteout, wdir, whiteout); > - if (!err) > - goto out; > - > - if (err != -EMLINK) { > + if (!IS_ERR(whiteout)) { > + err = ovl_do_link(ofs, ofs->whiteout, wdir, whiteout); > + if (err) { > + dput(whiteout); > + whiteout = ERR_PTR(err); > + } > + } > + inode_unlock(wdir); > + if (!IS_ERR(whiteout)) > + return whiteout; > + if (PTR_ERR(whiteout) != -EMLINK) { > pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%i)\n", > ofs->whiteout->d_inode->i_nlink, err); > ofs->no_shared_whiteout = true; > } > - dput(whiteout); > } > whiteout = ofs->whiteout; > ofs->whiteout = NULL; > -out: > - inode_unlock(wdir); > return whiteout; > } > > diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h > index afb7762f873f..4c1bae935ced 100644 > --- a/fs/overlayfs/ovl_entry.h > +++ b/fs/overlayfs/ovl_entry.h > @@ -88,6 +88,7 @@ struct ovl_fs { > /* Shared whiteout cache */ > struct dentry *whiteout; > bool no_shared_whiteout; > + struct mutex whiteout_lock; > /* r/o snapshot of upperdir sb's only taken on volatile mounts */ > errseq_t errseq; > }; > diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c > index f42488c01957..cb1a17c066cd 100644 > --- a/fs/overlayfs/params.c > +++ b/fs/overlayfs/params.c > @@ -797,6 +797,8 @@ int ovl_init_fs_context(struct fs_context *fc) > fc->s_fs_info = ofs; > fc->fs_private = ctx; > fc->ops = &ovl_context_ops; > + > + mutex_init(&ofs->whiteout_lock); > return 0; > > out_err: > -- > 2.49.0 >