On Sun, Jan 16, 2011 at 12:30 AM, Al Viro <v...@zeniv.linux.org.uk> wrote:
>
>> > AFAICS, it keeps your write-free objectives and gets much saner API.
>> > Shout if you have problems with that...
>>
>> No that sounds good, I don't have a problem with it, although I don't
>> exactly understand what you're getting at in the second paragraph.
>
> OK, I have a hopefully sane implementation in tip of #untested.
>
> There's a fun problem with what you do in do_lookup(), BTW.  Look:
> we enter do_lookup() with LOOKUP_RCU.  We find dentry in dcache,
> everything's beautiful.  The sucker has ->d_revalidate().  We go
> to need_revalidate.  There we call do_revalidate().  It calls
> d_revalidate(), which calls ->d_revalidate() and instead of spitting
> into your eye and returning -ECHILD it happily returns 1.  So
> do d_revalidate() and then do_revalidate(), without any further
> actions.  do_revalidate() has returned our dentry, which is neither
> NULL nor ERR_PTR(), so back in do_lookup() we go to done.
>
> There we set path->mnt and path->dentry and call __follow_mount().
> And damn, it *is* a mountpoint.  So we
>        * do dput() on dentry we'd never grabbed a reference to
>        * grab a reference to a different dentry (and remain in happy
> belief that we are in LOOKUP_RCU mode, and thus don't need to drop it)
>        * grab a reference to vfsmount (via lookup_mnt()).  Ditto (and
> I haven't checked if grabbing vfsmount_lock twice shared isn't a recipe
> for a deadlocky race with something grabbing it exclusive between these
> nested shared grabs).
>        * if we are really unlucky and that mountpoint is, in turn,
> overmounted, we'll hit mntput().  While under vfsmount_lock.
>
> AFAICS, it's badly b0rken.  And autofs really steps into that mess.

Ah, I think you're right there, good catch.


> As minimum, we'd need to split need_revalidate: and done: in RCU and non-RCU
> variants.  I'm about to fall down right now after an all-nighter (and then
> some); if you put something together before I get up, please throw it
> my way.

It also has a problem with jumping back to need_lookup case there too.
I think separating out those two cases is reasonable.

>
> Note that the problem exists both in mainline and in mainline+automount
> patches; in the latter it's a bit nastier, but in principle the situation
> is the same, so I'd rather see a fix for that in front of automount queue.

Definitely agree.

I'm on the road at the moment so would much appreciate if you can
cut the patch, but I could suggest something along the lines of:

        if (nd->flags & LOOKUP_RCU) {
                unsigned seq;

                *inode = nd->inode;
                dentry = __d_lookup_rcu(parent, name, &seq, inode);
                if (!dentry)
                        goto need_lookup_rcu;
                /* Memory barrier in read_seqcount_begin of child is enough */
                if (__read_seqcount_retry(&parent->d_seq, nd->seq))
                        return -ECHILD;

                nd->seq = seq;
                if (dentry->d_flags & DCACHE_OP_REVALIDATE) {
                                dentry = do_revalidate(dentry, nd);
                                if (!dentry)
                                       goto need_lookup_rcu;
                                if (IS_ERR(dentry))
                                       goto fail;
                }
                path->mnt = mnt;
                path->dentry = dentry;
                __follow_mount_rcu(nd, path, inode);
        } else {
                dentry = __d_lookup(parent, name);
                if (!dentry)
                        goto need_lookup;
found:
                if (dentry->d_flags & DCACHE_OP_REVALIDATE) {
                                dentry = do_revalidate(dentry, nd);
                                if (!dentry)
                                       goto need_lookup;
                                if (IS_ERR(dentry))
                                       goto fail;
                }
                path->mnt = mnt;
                path->dentry = dentry;
                __follow_mount(path);
                *inode = path->dentry->d_inode;
        }
        return 0;

need_lookup_rcu:
        if (nameidata_drop_rcu(nd))
                return -ECHILD;
need_lookup:
       ...

_______________________________________________
autofs mailing list
autofs@linux.kernel.org
http://linux.kernel.org/mailman/listinfo/autofs

Reply via email to