Re: [RFC][overlayfs] do we still need d_instantiate_anon() and export of d_alloc_anon()?

Amir Goldstein
2023-11-11T18:31:26Z

On Sat, Nov 11, 2023 at 10:04 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> AFAICS, the main reason for exposing those used to be the need
> to store ovl_entry in allocated dentry; we needed to do that before it
> gets attached to inode, so the guts of d_obtain_alias() had to be
> exposed.
>
> These days overlayfs is stashing ovl_entry in the inode, so
> we are left with this:
> dentry = d_find_any_alias(inode);
> if (dentry)
> goto out_iput;
>
> dentry = d_alloc_anon(inode->i_sb);
> if (unlikely(!dentry))
> goto nomem;
>
> if (upper_alias)
> ovl_dentry_set_upper_alias(dentry);
>
> ovl_dentry_init_reval(dentry, upper, OVL_I_E(inode));
>
> return d_instantiate_anon(dentry, inode);
>
> ovl_dentry_init_reval() can bloody well be skipped, AFAICS - all it does
> is potentially clearing DCACHE_OP_{,WEAK_}REVALIDATE. That's also done
> in ovl_lookup(), and in case we have d_splice_alias() return a non-NULL
> dentry we can simply copy it there. Sure, somebody might race with
> us, pick dentry from hash and call ->d_revalidate() before we notice that
> DCACHE_OP_REVALIDATE could be cleaned. So what? That call of ->d_revalidate()
> will find nothing to do and return 1. Which is the effect of having
> DCACHE_OP_REVALIDATE cleared, except for pointless method call. Anyone
> who finds that dentry after the flag is cleared will skip the call.
> IOW, that race is harmless.

Just a minute.
Do you know that ovl_obtain_alias() is *only* used to obtain a disconnected
non-dir overlayfs dentry?

I think that makes all the analysis regarding race with d_splice_alias()
moot. Right?

Do DCACHE_OP_*REVALIDATE even matter for a disconnected
non-dir dentry?

>
> And as for the ovl_dentry_set_upper_alias()... that information used to
> live in ovl_entry until the need to trim the thing down. These days
> it's in a bit in dentry->d_fsdata.
>
> How painful would it be to switch to storing that in LSB of ovl_entry::__numlower,
> turning ovl_numlower() into
> return oe ? oe->__numlower>>1 : 0
> and ovl_lowerdata() into
> return lowerstack ? &lowerstack[(oe->__numlower>>1) - 1] : NULL
> with obvious adjustment to ovl_alloc_entry().
>
> An entry is coallocated with an array of struct ovl_path, with
> numlower elements. More than 2G layers doesn't seem to be plausible -
> there are fat 64bit boxen, but 32Gb (kmalloc'ed, at that) just in
> the root ovl_entry alone feels somewhat over the top ;-)
>
> So stealing that bit shouldn't be a problem. Is there anything I'm
> missing?

You are missing that the OVL_E_UPPER_ALIAS flag is a property of
the overlay dentry, not a property of the inode.

N lower hardlinks, the first copy up created an upper inode
all the rest of the N upper aliases to that upper inode are
created lazily.

However, for obvious reasons, OVL_E_UPPER_ALIAS is not
well defined for a disconnected overlay dentry.
There should not be any code (I hope) that cares about
OVL_E_UPPER_ALIAS for a disconnected overlay dentry,
so I *think* ovl_dentry_set_upper_alias() in this code is moot.

I need to look closer to verify, but please confirm my assumption
regarding the irrelevance of DCACHE_OP_*REVALIDATE for a
disconnected non-dir dentry.

Thanks,
Amir.